vue 2 迁移到 vue 3 细节总结

Vue.js 3.0 在北京时间 2020 年 9 月 19 日凌晨,终于发布了 3.0 版本,代号:One Piece。

查看 vue 3.0 文档 后,总结记录一下从 vue 2 迁移到 vue 3 所需的一些更改,方便日后对比学习新版本。

与 vue 2.x 比较,将 vue 3.0 中的改动分类为 3 种:

  1. new:新增部分;
  2. breaking: 具有破坏性的部分。对 vue 2.x 中的对应功能的语法、语义等做出了调整;
  3. removed:从 Vue 3.0 中删除,不再受支持。

下面主要记录 vue 3.0 中的写法。

文章目录

      • 一、 new
        • 1. 异步组件
        • 2. 定制内置元素
        • 3. 片段
      • 二、 breaking
        • 1. v-for 中的 Ref 数组
        • 2. 自定义指令
        • 3. 自主定制元素
        • 4. Data 选项
        • 5. 事件 API
        • 6. 函数式组件
        • 7. 全局 API
        • 8. 全局 API Treeshaking
        • 9. 内敛模板 Attribute
        • 10. key attribute
        • 11. 按键修饰符
        • 12. 在 prop 的默认函数中访问 this
        • 13. 渲染函数 API
        • 14. Slot 统一
        • 15. 过渡的 class 名更改
        • 16. v-model
        • 17. v-if 与 v-for 的优先级对比
        • 18. v-bind 合并行为
      • 三、removed
        • 1. 过滤器

一、 new

1. 异步组件

新增了 defineAsyncComponent 方法,用于显式地定义异步组件。

import {
      defineAsyncComponent } from 'vue'
import ErrorComponent from './components/ErrorComponent.vue'
import LoadingComponent from './components/LoadingComponent.vue'

// 不带选项的异步组件
const asyncPage = defineAsyncComponent(() => import('./NextPage.vue'))

// 带选项的异步组件
const asyncPageWithOptions = defineAsyncComponent({
     
  loader: () => import('./NextPage.vue'),
  delay: 200,
  timeout: 3000,
  errorComponent: ErrorComponent,
  loadingComponent: LoadingComponent
})

2. 定制内置元素

针对 DOM 模板,使用 v-is 来动态渲染组件:

<tr v-is="'blog-post-row'">tr>

3. 片段

在 vue 3 中,组件支持多根节点组件,即片段。


<template>
  <header>...header>
  <main v-bind="$attrs">...main>
  <footer>...footer>
template>

二、 breaking

1. v-for 中的 Ref 数组

v-for 中的 ref 绑定为一个函数,而不是直接的一个数组。

<div v-for="item in list" :ref="setItemRef">div>
export default {
     
  data() {
     
    return {
     
      itemRefs: []
    }
  },
  methods: {
     
    setItemRef(el) {
     
      this.itemRefs.push(el)
    }
  },
  beforeUpdate() {
     
    this.itemRefs = []
  },
  updated() {
     
    console.log(this.itemRefs)
  }
}

// 或者
import {
      ref, onBeforeUpdate, onUpdated } from 'vue'

export default {
     
  setup() {
     
    let itemRefs = []
    const setItemRef = el => {
     
      itemRefs.push(el)
    }
    onBeforeUpdate(() => {
     
      itemRefs = []
    })
    onUpdated(() => {
     
      console.log(itemRefs)
    })
    return {
     
      itemRefs,
      setItemRef
    }
  }
}

2. 自定义指令

重命名自定义指令 API,以便更好地与组件生命周期保持一致。

const MyDirective = {
     
  beforeMount(el, binding, vnode, prevVnode) {
     },
  mounted() {
     },
  beforeUpdate() {
     },
  updated() {
     },
  beforeUnmount() {
     }, // 新
  unmounted() {
     }
}

3. 自主定制元素

is 特性用法仅限于保留的 标记,在其它标记上将失效。

对于 SFC(单文件组件),is prop 的使用格式为:

// 在 vue 3 中渲染的将会是普通的 button
<button is="plastic-button">点击我!button>

4. Data 选项

data 组件选项声明不再接收纯 JavaScript object,而需要 function 声明。

<script>
  import {
       createApp } from 'vue'

  createApp({
      
    data() {
      
      return {
      
        apiKey: 'a1b2c3'
      }
    }
  }).mount('#app')
script>

当来自组件的 data() 及其 mixin 或 extends 基类被合并时,现在将浅层次执行合并。

5. 事件 API

$on,$off 和 $once 实例方法已被移除,$emit 尚保留为一个 API。

现可以通过外部库 mitt 来替换现有的事件中心。

或者,在兼容性构建中,也可以支持这些方法。

6. 函数式组件

在 Vue 3 中,所有的函数式组件都是用普通函数创建的,换句话说,不需要定义 { functional: true } 组件选项。

import {
      h } from 'vue'

const DynamicHeading = (props, context) => {
     
  return h(`h${
       props.level}`, context.attrs, context.slots)
}

DynamicHeading.props = ['level']

export default DynamicHeading

7. 全局 API

调用 createApp 返回一个应用实例,这是 Vue 3 中的新概念:

import {
      createApp } from 'vue'

const app = createApp({
     })

应用程序实例暴露当前全局 API 的子集,经验法则是,任何全局改变 Vue 行为的 API 现在都会移动到应用实例上,以下是当前全局 API 及其相应实例 API 的表:

2.x 全局 API 3.x 实例 API(app)
Vue.config app.config
Vue.config.productionTip removed
Vue.config.ignoredElements app.config.isCustomElement
Vue.component app.component
Vue.directive app.directive
Vue.mixin app.mixin
Vue.use app.use

所有其他不全局改变行为的全局 API 现在被命名为 exports

在应用程序之间共享配置 (如组件或指令) 的一种方法是创建工厂功能,如下所示:

import {
      createApp } from 'vue'
import Foo from './Foo.vue'
import Bar from './Bar.vue'

const createMyApp = options => {
     
  const app = createApp(options)
  app.directive('focus' /* ... */)

  return app
}

createMyApp(Foo).mount('#foo')
createMyApp(Bar).mount('#bar')

8. 全局 API Treeshaking

在 vue 2.x 中,tree-shaking 针对全局 API Vue.nextTick() 无计可施。

在 Vue 3 中,全局和内部 API 都经过了重构,并考虑到了 tree-shaking 的支持。因此,全局 API 现在只能作为 ES 模块构建的命名导出进行访问。

import {
      nextTick } from 'vue'

nextTick(() => {
     
  // 一些和DOM有关的东西
})
import {
      shallowMount } from '@vue/test-utils'
import {
      MyComponent } from './MyComponent.vue'
import {
      nextTick } from 'vue'

test('an async feature', async () => {
     
  const wrapper = shallowMount(MyComponent)

  // 执行一些DOM相关的任务

  await nextTick()

  // 运行你的断言
})

9. 内敛模板 Attribute

在 vue 3.0 中,对内联特性(inline-template attribute)的支持已经废除。

现在,有 2 种迁移策略:

  1. 使用
<script type="text/html" id="my-comp-template">
  <div>{
     {
      hello }}</div>
</script>

// 在组件中,使用选择器将模板作为目标:
const MyComp = {
     
  template: '#my-comp-template'
  // ...
}
  1. 默认 Slot
<!-- 2.x 语法 -->
<my-comp inline-template :msg="parentMsg">
  {
     {
      msg }} {
     {
      childState }}
</my-comp>

<!-- 默认 Slot 版本 -->
<my-comp v-slot="{ childState }">
  {
     {
      parentMsg }} {
     {
      childState }}
</my-comp>

10. key attribute

key attribute 被用于提示 Vue 的虚拟 DOM 算法对节点身份保持持续的追踪,以便 vue 知道在何时能够重用节点、何时能够修改节点、何时能够对节点重新排序或者重新创建。

  1. vue 3.0 中,会自动为每个分支生成唯一的 key,不推荐自己为分支设置 key 了。但是,为了兼容,设置 key 也是可以的,但是一定要保证 key 的唯一性。
<!-- Vue 2.x -->
<div v-if="condition" key="a">Yes</div>
<div v-else key="a">No</div>

<!-- Vue 3.x (recommended solution: remove keys) -->
<div v-if="condition">Yes</div>
<div v-else>No</div>

<!-- Vue 3.x (alternate solution: make sure the keys are always unique) -->
<div v-if="condition" key="a">Yes</div>
<div v-else key="b">No</div>
  1. 在 vue 3.0 中, 当使用 时,key 应该被设置在