Vue3 出来已经有一段时间了,昨天翻了一遍官方文档,今天尝试了一下,顺便做个与 Vue2 的差异记录。

关于 Vue3 包的体积,性能上的区别我就不再多说,因为这对我们使用者来说是没得选的,性能有提升更好,没提升之前 Vue2 的版本不也照样用嘛。所以这篇文章主要说开发体验上的区别。

创建应用

我们从创建应用说起,Vue2 使用的是 new Vue 的方式创建应用,而 Vue3 使用的是调用 Vue 实例上的 Vue.createApp 方法创建。

1
2
3
4
5
6
7
8
9
10
11
// 2.x
const vm = new Vue({
//...options
})
vm.$mount('#app')

// 3.x
const vm = Vue.createApp({
//...options
})
vm.mount('#app')

Vue3 中一些全局的方法可以链式调用了:

1
2
3
4
5
const app = Vue.createApp({})
app.component('component', component)
.directive('directive', directive)
.use(plugin)
app.mount('#app')

⚠️注意别这么用:

1
2
3
4
5
// 错误用法
const app = Vue.createApp({}).mount('#app')
app.component('component', component)
.directive('directive', directive)
.use(plugin)

因为 moun 返回的是根组件实例,而不是应用实例

扩展属性区别

Vue3 中 ,全局 confg 中新增了一个 globalProperties 属性,它是一个对象,对象里面的属性全局都可以访问到。

1
2
3
4
5
6
7
// Vue2 中在 Vue 上挂载 axios
import axios from 'axios'
Vue.prototype.$axios = axios

// Vue3 中在 Vue 上挂载 axios
const app = Vue.createApp({})
app.config.globalProperties.$axios = axios

注册组件区别

1
2
3
4
5
6
// Vue2 中注册组件
Vue.component('component-name', { ...options })

// Vue3 中注册组件
const app = Vue.createApp({})
app.component('component-name', { ...options })

Vue3 的组件注册方式解决了 Vue2 的全局组件污染问题。

1
2
3
4
5
6
7
8
<body>
<div id="app">
<test></test>
</div>
<div id="app2">
<test></test>
</div>
</body>
1
2
3
4
5
6
7
8
// Vue2
Vue.component('test',{template: '<div>test-component</div>'})
const vm = new Vue({
el: '#app',
})
const vm2 = new Vue({
el: '#app2',
})

可以看到 test 组件只注册了一次,但是 vmvm2 都可以使用 test 组件。这可能会不经意导致组件名称冲突组件被覆盖。

而 Vue3 则不会出现这种情况:

1
2
3
4
5
6
const app = Vue.createApp({})
app.component('test',{template: '<div>app1</div>'})
app.mount('#app')
const app2 = Vue.createApp({})
app2.component('test',{template: '<div>app2</div>'})
app2.mount('#app2')

Vue.component 只是一个代表,其实类似这样的 API,比如Vue.directiveVue.mixin 都存在这样的问题。Vue3 已经全都规避掉了。

接下来其实你完全可以按照 Vue2 的习惯来编写代码,Vue3 并没有抛弃 Vue2 的写法,不过有一点需要注意的是,Vue2 和 Vue3 的生命周期钩子有所区别:

  • Vue2 中的 beforeDestroy 变成了 beforeUnmount
  • VUe2 中的 destroyed 变成了 unmounted
  • Vue3 中新增了为了方便调试的 renderTrackedrenderTriggered 钩子

还有个新特性,就是Vue3 终于不限制单一跟节点了!!!

组合式 API

Vue3 对开发者来说最大的区别应该就是 组合式 API 了吧(至少对我来说是的)。

setup

Vue3 中新增了一个 setup() 选项,官方文档上是这样定义它的:

一个组件选项,在创建组件之前执行,一旦 props 被解析,并作为组合式 API 的入口点

setup 接受两个参数,第一个为 props, 第二个是 contextprops 很好理解,这个 context 包含的内容我们打印看下:

props 解析后 setup 就会被调用,那么这么说来,它应该比 beforeCreate 钩子早执行,验证一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
// html 省略
const vm = Vue.createApp({
setup(props, context){
console.log(3)
},
beforeCreate() {
console.log(1)
},
created() {
console.log(2)
},
})
vm.mount('#app')

执行结果:

没有猜错,早于 beforeCreate 钩子执行。

在 setup 内部创建响应式数据

1
2
3
4
5
<body>
<div id="app">
{{v}}{{c.a}}{{obj.a}}
</div>
</body>

setup 内部使用或修改响应式数据,不能单纯地使用直接用变量名,而是需要用 .value 的方式取值。但是在外部使用的时候,Vue 会自动帮你展开,所以在模板或其他地方使用的时候,直接使用变量就行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const options = {
setup(){
// 创建响应式数据

// 单个
const v = Vue.ref(0)
console.log(v.value) // 0

// 使用 ref 创建的响应式对象
const c = Vue.ref({a: 1})
console.log(c.value.a) // 1

// 响应式对象,外部可通过 obj.a 修改 obj的属性
const obj = Vue.reactive({
a: 1,
b: 2
})
return {
v,
c,
obj
}
},
created(){
// 修改 v 与 obj.a 的值
this.v = 3
this.c.a = 2
this.obj.a = 0
}
}
Vue.createApp(options).mount('#app')

上面主要说的是 refreactive,下面说 toReftoRefs

toRef 用于为为源响应式对象上的 property 新创建一个 ref。它接受两个参数,第一个是响应式对象,第二个是要提出来的属性名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const options = {
setup(){
const obj = Vue.reactive({
a: 1
})
const obja = Vue.toRef(obj, 'a')
const b_obja = obj.a
return {
obj,
obja,
b_obja
}
},
created(){
console.log(this.obja)
console.log(this.obj.a)
this.obja = 2
console.log(this.obja)
console.log(this.obj.a)
this.obj.a = 3
console.log(this.obja)
console.log(this.obj.a)
}
}
Vue.createApp(options).mount('#app')

运行结果:

由此可见,虽然将 a 从 obj 中剥离了出来,但是它们之间还是保持着响应式连接,因此当改变 obja 或 obj.a 的值的时候,双方的值都会相应改变。

toRefs 则是将一个响应式对象转换为普通对象,其中结果对象的每个属性都是指向原始对象相应属性的 ref。说白了就是 toRef 的一个批量快捷操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const options = {
setup(props) {
const {name, age} = Vue.toRefs(props)
const obj = Vue.reactive({
a: 1,
b: 2
})
const b_obj = Vue.toRefs(obj)
return {
...b_obj, name, age, obj
}
},
created(){
console.log(this.a, this.b, this.name, this.age, this.obj)
this.obj.a = 3
console.log(this.a, this.obj.a)
this.a = 4
console.log(this.a, this.obj.a)
},
}
//在这传入 props
Vue.createApp(options, { name: '张三', age: 3 }).mount('#app')

执行结果:

可以看出 a 与 obj.a 也依旧保持着响应式连接。

Vue3.0 新特性学习(二)