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

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

创建应用

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

// 2.x
const vm = new Vue({
  //...options
})
vm.$mount('#app')

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

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

const app = Vue.createApp({})
app.component('component', component)
  .directive('directive', directive)
  .use(plugin)
app.mount('#app')

⚠️注意别这么用:

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

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

扩展属性区别

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

// Vue2 中在 Vue 上挂载 axios
import axios from 'axios'
Vue.prototype.$axios = axios

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

注册组件区别

// Vue2 中注册组件
Vue.component('component-name', { ...options })

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

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

<body>
    <div id="app">
      <test></test>
    </div>
    <div id="app2">
      <test></test>
    </div>
</body>
// 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 则不会出现这种情况:

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 钩子早执行,验证一下:

// html 省略
const vm = Vue.createApp({
  setup(props, context){
    console.log(3)
  },
  beforeCreate() {
    console.log(1)
  },
  created() {
    console.log(2)
  },
})
vm.mount('#app')

执行结果:

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

在 setup 内部创建响应式数据

<body>
  <div id="app">
    {{v}}{{c.a}}{{obj.a}}
  </div>
</body>

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

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。它接受两个参数,第一个是响应式对象,第二个是要提出来的属性名。

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 的一个批量快捷操作。

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 新特性学习(二)