前言
Vue3.0 在去年9月正式发布了,也有许多小伙伴都热情的拥抱Vue3.0。去年年底我们新项目使用Vue3.0来开发,这篇文章就是在使用后的一个总结, 包含Vue3新特性的使用以及一些用法上的变更。
为什么要升级Vue3
使用Vue2.x的小伙伴都熟悉,Vue2.x中所有数据都是定义在data
中,方法定义在methods
中的,并且使用this
来调用对应的数据和方法。那Vue3.x中就可以不这么玩了, 具体怎么玩我们后续再说, 先说一下Vue2.x版本这么写有什么缺陷,所以才会进行升级变更的。
回顾Vue2.x实现加减
<template>
<div class="homePage">
<p>count: {
{ count }}</p>
<p>倍数: {
{ multiple }}</p>
<div>
<button style="margin-right:10px" @click="increase">加1</button>
<button @click="decrease">减一</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
count: 0,
};
},
computed: {
multiple() {
return 2 * this.count;
},
},
methods: {
increase() {
this.count++;
},
decrease() {
this.count++;
},
},
};
</script>
上面代码只是实现了对count
的加减以及显示倍数, 就需要分别在data、methods、computed中进行操作,当我们增加一个需求,就会出现下图的情况:
当我们业务复杂了就会大量出现上面的情况, 随着复杂度上升,就会出现这样一张图, 每个颜色的方块表示一个功能:
甚至一个功能还有会依赖其他功能,全搅合在一起。
当这个组件的代码超过几百行时,这时增加或者修改某个需求, 就要在data、methods、computed以及mounted中反复的跳转,这其中的的痛苦写过的都知道。
那我们就想啊, 如果可以按照逻辑进行分割,将上面这张图变成下边这张图,是不是就清晰很多了呢, 这样的代码可读性和可维护性都更高:
那么vue2.x版本给出的解决方案就是Mixin, 但是使用Mixin也会遇到让人苦恼的问题:
-
命名冲突问题
-
不清楚暴露出来的变量的作用
-
逻辑重用到其他 component 经常遇到问题
关于上面经常出现的问题我就不一一举例了,使用过的小伙伴多多少少都会遇到。文章的重点不是Mixin,如果确实想知道的就留言啦~
所以,我们Vue3.x就推出了Composition API
主要就是为了解决上面的问题,将零散分布的逻辑组合在一起来维护,并且还可以将单独的功能逻辑拆分成单独的文件。接下来我们就重点认识Composition API
。
Composition API
setup
setup 是Vue3.x新增的一个选项, 他是组件内使用 Composition API
的入口。
setup执行时机
我在学习过程中看到很多文章都说setup 是在 beforeCreate
和created
之间, 这个结论是错误的。实践是检验真理的唯一标准, 于是自己去检验了一下:
export default defineComponent ({
beforeCreate() {
console.log("----beforeCreate----");
},
created() {
console.log("----created----");
},
setup() {
console.log("----setup----");
},
})
setup 执行时机是在beforeCreate之前执行,详细的可以看后面生命周期讲解。
::: warning 由于在执行setup
时尚未创建组件实例,因此在 setup
选项中没有 this
。:::
setup 参数
使用setup
时,它接受两个参数:
-
props: 组件传入的属性
-
context
setup中接受的props
是响应式的, 当传入新的props 时,会及时被更新。由于是响应式的, 所以不可以使用ES6解构,解构会消除它的响应式。
错误代码示例, 这段代码会让props不再支持响应式:
// demo.vue
export default defineComponent ({
setup(props, context) {
const { name } = props
console.log(name)
},
})
那在开发中我们想要使用解构,还能保持props
的响应式,有没有办法解决呢?大家可以思考一下,在后面toRefs
学习的地方为大家解答。
接下来我们来说一下setup
接受的第二个参数context
,我们前面说了setup
中不能访问Vue2中最常用的this
对象,所以context
中就提供了this
中最常用的三个属性:attrs
、slot
和emit
,分别对应Vue2.x中的 $attr
属性、slot
插槽 和$emit
发射事件。并且这几个属性都是自动同步最新的值,所以我们每次使用拿到的都是最新值。
reactive、ref与toRefs
在vue2.x中, 定义数据都是在data
中, 但是Vue3.x 可以使用reactive
和ref
来进行数据定义。
那么ref
和reactive
他们有什么区别呢?分别什么时候使用呢?说到这里,我又不得不提一下,看到很多网上文章说(reactive
用于处理对象的双向绑定,ref
则处理js基础类型的双向绑定)。我其实不太赞同这样的说法,这样很容易初学者认为ref
就能处理js基本类型, 比如ref
也是可以定义对象的双向绑定的啊, 上段代码:
setup() {
const obj = ref({count:1, name:"张三"})
setTimeout(() =>{
obj.value.count = obj.value.count + 1
obj.value.name = "李四"
}, 1000)
return{
obj
}
}
我们将obj.count
和obj.name
绑定到页面上也是可以的;但是reactive
函数确实可以代理一个对象, 但是不能代理基本类型,例如字符串、数字、boolean等。
接下来使用代码展示一下ref
、reactive
的使用:
运行效果:
上面的代码中,我们绑定到页面是通过user.name
,user.age
;这样写感觉很繁琐,我们能不能直接将user
中的属性解构出来使用呢?答案是不能直接对user
进行结构, 这样会消除它的响应式, 这里就和上面我们说props
不能使用ES6直接解构就呼应上了。那我们就想使用解构后的数据怎么办,解决办法就是使用toRefs
。
toRefs用于将一个reactive对象转化为属性全部为ref对象的普通对象。具体使用方式如下:
<template>
<div class="homePage">
<p>第 {
{ year }} 年</p>
<p>姓名: {
{ nickname }}</p>
<p>年龄