Home  >  Article  >  Web Front-end  >  Vue3 development example code analysis

Vue3 development example code analysis

王林
王林forward
2023-05-17 09:44:181676browse

Get this

The use of this in each component in Vue2 points to the current component instance. This also contains globally mounted things. Everyone knows that this.xxx has everything.

There is no this in Vue3. If you want similar usage, there are two ways. One is to get the current component instance, and the other is to get the global instance. You can print it out and see for yourself

<script setup>
import { getCurrentInstance } from &#39;vue&#39;

// proxy 就是当前组件实例,可以理解为组件级别的 this,没有全局的、路由、状态管理之类的
const { proxy, appContext } = getCurrentInstance()

// 这个 global 就是全局实例
const global = appContext.config.globalProperties
</script>

Global registration (properties/methods)

In Vue2, what we need to mount globally is usually as follows, and then in all components, we can get <pre class="brush:js;">Vue.prototype.xxx = xxx</pre> through this.xxx

In Vue3, it cannot be written like this. It is replaced by a global object that can be accessed by all components, which is the object of the global instance mentioned above. For example, do global registration in main.js

// main.js
import { createApp } from &#39;vue&#39;
import App from &#39;./App.vue&#39;
const app = createApp(App)
// 添加全局属性
app.config.globalProperties.name = &#39;沐华&#39;

Call in other components

<script setup>
import { getCurrentInstance } from &#39;vue&#39;
const { appContext } = getCurrentInstance()

const global = appContext.config.globalProperties
console.log(global.name) // 沐华
</script>

Get DOM

<template>
    <el-form ref="formRef"></el-form>
    <child-component />
</template>
<script setup lang="ts">
import ChildComponent from &#39;./child.vue&#39;
import { getCurrentInstance } from &#39;vue&#39;
import { ElForm } from &#39;element-plus&#39;

// 方法一,这个变量名和 DOM 上的 ref 属性必须同名,会自动形成绑定
const formRef = ref(null)
console.log(formRef.value) // 这就获取到 DOM 了

// 方法二
const { proxy } = getCurrentInstance()
proxy.$refs.formRef.validate((valid) => { ... })

// 方法三,比如在 ts 里,可以直接获取到组件类型
// 可以这样获取子组件
const formRef = ref<InstanceType<typeof ChildComponent>>()
// 也可以这样 获取 element ui 的组件类型
const formRef = ref<InstanceType<typeof ElForm>>()
formRef.value?.validate((valid) => { ... })
</script>

Initialize

Enter the page in Vue2 and request the interface, or some other initialization operations, Generally placed in created or mounted, but in Vue3, the two hooks beforeCreated and created are not needed because setup is before these two hooks. Execution, if you need these two, it will be superfluous

So any content you used in the beforeCreated / created / beforeMounted / mounted hooks will be placed in Vue3 setup, or put it in onMounted

<script setup>
import { onMounted } from &#39;vue&#39;

// 请求接口函数
const getData = () => {
    xxxApi.then(() => { ... })
}

onMounted(() => {
    getData()
})
</script>

Unbinding

There are generally two operations in Vue2 such as clearing timers and monitoring. Two methods:

  • One is to use $once with hook: BeforeDestroy, this Vue3 does not support

  • The second is to use the two hooks beforeDestroy / deactivated. In Vue3, the hook function is just renamed

<script setup>
import { onBeforeUnmount, onDeactivated } from &#39;vue&#39;

// 组件卸载前,对应 Vue2 的 beforeDestroy
onBeforeUnmount(() => {
    clearTimeout(timer)
    window.removeAddEventListener(&#39;...&#39;)
})

// 退出缓存组件,对应 Vue2 的 deactivated
onDeactivated(() => {
    clearTimeout(timer)
    window.removeAddEventListener(&#39;...&#39;)
})
</script>

ref and reactive

Both of these are used to create responsive objects, ref is usually used to create basic types, reactive is usually used to create responsive objects, this is officially recommended, in reality Not necessarily. Some people also use ref to define arrays, and some people only define one reactive for a component, and all the data is placed in it, just like Vue2's data , some people also use

. There are two things you need to know:

  • ref If the reference type is passed in, the internal source code will also be calledreactive is implemented by

  • ref. The returned attributes are used in template, just use them directly, but in JS When used, it needs to be obtained through .value, as follows. Because ref returns a wrapper object

<template>
    <div>{{ count }}</div>
</template>
<script setup>
import { ref, reactive } from &#39;vue&#39;
const count = ref(1)

// 有人这么用 
const arr = ref([])
console.log(arr.value) // []

// 也有人这么用,一个组件里所有的属性全部定义在一个对象里,有点 Vue2 data 的味道
const data = reactive({
    name: &#39;沐华&#39;,
    age: 18,
    ...
})
console.log(data.name) // 沐华

// 也有人一个组件里 ref 和 reactive 两种都用,随便你
</script>

Why does ref return a wrapper object? We all know that data in Vue2 returns an object because the object reference type can be used as a proxy or hijack. If only the basic type is returned, it is stored in the stack and recycled after execution in the execution stack. , there is no way to add a proxy or hijack, and naturally there is no way to track subsequent changes, so you have to return an object so that you can have responsive

toRef and toRefs

The two common points are to use To create responsive references, it is mainly used to extract attributes from responsive objects, or deconstruct responsive objects. The deconstructed attribute values ​​are still responsive attributes. If you do not use these two direct deconstructions, the responsive effect will be lost.

The main purpose is to facilitate us to use direct variables

xxx

instead of data.xxx. And when we modify xxx, we also directly modify the properties of the source object. The difference between the two: with s and without s, is the singular and plural, which means taking one and taking one. Duile<pre class="brush:php;toolbar:false">&lt;script setup&gt; import { reactive, toRef, toRefs } from &amp;#39;vue&amp;#39; const data = reactive({ name: &amp;#39;沐华&amp;#39;, age: 18 }) // 这样虽然能拿到 name / age,但是会变成普通变量,没有响应式效果了 const { name, age } = data // 取出来一个响应式属性 const name = toRef(data, &amp;#39;name&amp;#39;) // 这样解构出来的所有属性都是有响应式的 const { name, age } = toRefs(data) // 不管是 toRef 还是 toRefs,这样修改是会把 data 里的 name 改掉的 // 就是会改变源对象属性,这才是响应式该有的样子 name.value = &amp;#39;沐沐华华&amp;#39; &lt;/script&gt;</pre>watch

watch is used to monitor an existing property and do certain operations when it changes. There are three commonly used writing methods in Vue2 as follows

watch: {
    userId: &#39;getData&#39;,
    userName (newName, oldName) {
        this.getData()
    },
    userInfo: {
        handler (newVal, newVal) { this.getData() },
        immediate: true,
        deep: true
    }
}

The writing method of monitoring in Vue3 is much richer

Vue3's watch is a function that can receive three parameters. The first parameter is the property of monitoring, and the second parameter is the callback that receives the new value and the old value. Function, the third parameter is the configuration item

<script setup>
import { watch, ref, reactive } from &#39;vue&#39;

const name = ref(&#39;沐华&#39;)
const data = reactive({
    age: 18,
    money: 100000000000000000000,
    children: []
})

// 监听 ref 属性
watch(name, (newName, oldName) => { ... })

// 监听其他属性、路由或者状态管理的都这样
watch(
    () => data.age, 
    (newAge, oldAge) => { ... }
)

// 监听多个属性,数组放多个值,返回的新值和老值也是数组形式
watch([data.age, data.money], ([newAge, oldAge], [newMoney, oldMoney]) => { ... })

// 第三个参数是一个对象,为可配置项,有5个可配置属性
watch(data.children, (newList, oldList) => { ... }, {
    // 这两个和 Vue2 一样,没啥说的
    immediate: true,
    deep: true,
    // 回调函数的执行时机,默认在组件更新之前调用。更新后调用改成post
    flush: &#39;pre&#39;, // 默认值是 pre,可改成 post 或 sync
    // 下面两个是调试用的
    onTrack (e) { debugger }
    onTrigger (e) { debugger }
})
</script>

Regarding side effects, the third parameter can be received in the watch callback function, which is a function to clear side effects. Its execution mechanism is called before updating by default, such as the following code, When the key triggers an update,

222

will be printed first and then

Muhua

. If you need to call it after the update, you can add flush: post# in the third configuration item of watch. ##,

// 回调函数接收一个参数,为清除副作用的函数
watch(key, (newKey, oldKey, onInvalidate) => {
    console.log(&#39;沐华&#39;)
    // 获取DOM默认获取到的是更新前的dom,如果是flush: post,可以获取到更新后的dom
    console.log(&#39;DOM节点:&#39;, dom.innterHTML)
    onInvalidate(() => {
        console.log(2222)
    })
})
The monitoring is not over yetYouda: Hehe~

watchEffect

In addition to watch, Vue3 also adds a watchEffect. The difference is:

watch monitors one or more incoming values. When triggered, it will return the new value and the old value, and will not be executed for the first time by default

  • watchEffect is to pass in an immediate execution function, so it will be executed for the first time by default, and there is no need to pass in the monitoring content. The data source in the function will be automatically collected as a dependency. When the dependency changes The function will be re-executed (a bit like computed), and the new value and old value will not be returned

  • 清除副作用和副作用的刷新时机是一样的,区别是 watch 中会作为回调的第三个参数传入,watchEffect 中是回调函数的第一个参数

  • 正常情况下组件销毁/卸载后这两都会自动停止监听,但也有例,比如异步的方式在 setTimeout 里创建的监听就都需要手动停止监听,停止方式如下

// 停止监听
const unwatch = watch(&#39;key&#39;, callback)
const unwatchEffect = watchEffect(() => {})
// 需要的时候手动停止监听
unwatch()
unwatchEffect()

watchEffect 使用:

<script setup>
import { watchEffect } from &#39;vue&#39;

// 正常使用
watchEffect(() => {
    // 会自动收集这个函数使用到的属性作为依赖,进行监听
    // 监听的是 userInfo.name 属性,不会监听 userInfo
    console.log(userInfo.name)
})

// 有两个参数,参数一是触发监听回调函数,参数二是可选配置项
watchEffect(() => {...}, {
    // 这里是可配置项,意思和 watch 是一样的,不过这只有3个可配置的
    flush: &#39;pre&#39;,
    onTrack (e) { debugger }
    onTrigger (e) { debugger }
})

// 回调函数接收一个参数,为清除副作用的函数,和 watch 的同理
watchEffect(onInvalidate => {
    console.log(&#39;沐华&#39;)
    onInvalidate(() => {
        console.log(2222)
    })
})
</script>

watchEffect 如果需要修改配置项 flush 为 post 或 sync 时,可以直接使用别名,如下

watchEffect(() => {...}, {
    flush: &#39;post&#39;,
})
// 和下面这个是一样的
watchPostEffect(() => {})
-----------------------------
watchEffect(() => {...}, {
    flush: &#39;sync&#39;,
})
// 和下面这个是一样的
watchSyncEffect(() => {})

computed

Vue2 中 computed 最见的使用场景一般有: mapGetters/mapState 获取状态管理的属性、 获取 url 上的属性、条件判断、类型转换之类的,支持函数和对象两种写法

而 Vue3 中 computed 不再是一个对象,而是一个函数,用法其实基本差不多,函数第一个参数是侦听器源,用于返回计算的新值,也支持对象写法,第二个参数可用于调试

<script setup>
import { computed } from &#39;vue&#39;

// 获取 url 上的 type 属性
const type = computed(() => Number(this.$route.query.type || &#39;0&#39;))

// 对象写法
const visible = computed({
    get () { return this.visible },
    set (val) { this.$emit(&#39;input&#39;, val) }
})

// computed 第二个参数也是一个对象,调试用的
const hehe = computed(参数一上面两种都可, {
    onTrack (e) { debugger }
    onTrigger (e) { debugger }
})
</script>

nextTick

nextTick 的使用方法,除了不能用 this 其他的和 Vue2 一模一样,还是三种方式

<script setup>
import { nextTick} from &#39;vue&#39;

// 方式 一
const handleClick = async () => {
  await nextTick()
  console.log(&#39;沐华&#39;)
}

// 方式二
nextTick(() => {
    console.log(&#39;沐华&#39;)
})

// 方式三
nextTick().then(() => {
    console.log(&#39;沐华&#39;)
  })
</script>

mixins 和 hooks

Vue2 中逻辑的抽离复用一般用 mixins,缺点有三:

  • 没有独立命名空间,mixins 会和组件内部产生命名冲突

  • 不去翻代码不知道引入的 mixins 里面有啥

  • 引入多个 mixins 时不知道自己使用的是来自哪一个 mixins 的

在Vue3中,逻辑可重用的hooks语法实际上是一个函数,可以接受参数并返回值,从而被使用。在Vue3中,编写封装公共方法的方法与平时相同

// xxx.js
expport const getData = () => {}
export default function unInstance () {
    ...
    return {...}
}

// xxx.vue
import unInstance, { getData } from &#39;xx.js&#39;
const { ... } = unInstance()
onMounted(() => {
    getData()
})

关于 hooks 如何写出更优雅的代码,还个需要多写,多实践

组件通信

Vue3 组件通信的方式,有如下几种

  • props + defineProps

  • defineEmits

  • defineExpose / ref

  • useAttrs

  • v-model(支持多个)

  • provide / inject

  • Vuex / Pinia

关于 Vue 组件通信的使用方式,去年我写过一篇文章,上面都罗列的很详细了,就不搬过来了

Vue3的8种和Vue2的12种组件通信

多个 v-model

Vue2 中每个组件上只能写一个 v-model,子组件没有写 model 的话,默认在 props 接收 value 即可,修改就是用 this.$emit('input') 事件

Vue3 中每个组件每个组件上支持写多个 v-model,没有了 .syncmodel 重命名的操作,也不需要了,写v-model 的时候就需要把命名一起写上去了,如下:

// 父组件写法
<template>
    <child v-model:name="name" v-model:age="age" />
</template>
<script setup>
import { ref } from "vue"
const name = ref(&#39;沐华&#39;)
const age = ref(18)
</script>

// 子组件
<script setup>
const emit = defineEmits([&#39;update:name&#39;, &#39;update:age&#39;])

const handleClick = () => {
    console.log(&#39;点击了&#39;)
    emit(&#39;update:name&#39;, &#39;这是新的名字&#39;)
}
</script>

状态管理

Vuex 会的就不用说了,不会的就直接学 Pinia 吧

Pinia 的使用方式,我之前也写过一篇文章,也不搬过来了

上手 Vue 新的状态管理 Pinia,一篇文章就够了

路由

Vue2 中的 $route$router 如下,可以自己打印出来看一下

<script setup>
import { useRoute, useRouter } from "vue-router"

// route 对应 this.$route
const route = useRoute()

// router 对应 this.$router
const router = useRouter()
</script>

template

Vue2 中只能有一个根节点,而 Vue3 中支持多个根节点,这个大家都知道

其实本质上 Vue3 每个组件还是一个根节点,因为 DOM 树只能是树状结构的,只是 Vue3 在编译阶段新增了判断,如果当前组件不只一个根元素,就添加一个 fragment 组件把这个多根组件的给包起来,相当于这个组件还是只有一个根节点

<template>
    <div>1</div>
    <div>2</div>
</template>

CSS 样式穿透

Vue2 中在 scoped 中修改子组件或者组件库中的组件样式,改不了的情况下,就可以用样式穿透,不管是 Less 还是 SASS 都是用 /dee/ .class {} 来做样式穿透,而 Vue3 中就不支持 /deep/ 的写法了,换成 :deep(.class)

<style scoped>
// 这样写不生效的话
.el-form {
    .el-form-item { ... }
}
// Vue2 中这样写
/deep/ .el-form {
    .el-form-item { ... }
}
// Vue3 中这样写
:deep(.el-form) {
    .el-form-item { ... }
}
</style>

// 别再这样加一个没有 scoped 的 style 标签了,全都加到全局上去了
// <style lang="scss">
//  .el-form {
//     .el-form-item { ... }
//  }
// </style>

CSS 绑定 JS 变量

就是 CSS 中可以使用 JS 的变量来赋值,如下

<template>
    <div class="name">沐华</div>
</template>
<script setup>
import { ref } from "vue"
const str = ref(&#39;#f00&#39;) // 红色
</script>
<style scoped lang="scss">
.name {
    background-color: v-bind(str); // JS 中的色值变量 #f00 就赋值到这来了
}
</style>

The above is the detailed content of Vue3 development example code analysis. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete