一.《Vue 3 迁移指南》参考文档:/zh/
二.Vue 3 中需要关注的一些新特性。
1. 组合式 API*;
2. 单文件组件中的组合式 API 语法糖 (<script setup>)*;
3. Teleport 组件;
4. Fragments 片段;
5. Emits 组件选项**;
6. 来自 @vue/runtime-core 的 createRenderer API 用来创建自定义渲染函数;
7. 单文件组件中的状态驱动的 CSS 变量 (<style> 中的 v-bind)*;
8. SFC <style scoped> 新增全局规则和针对插槽内容的规则;
9. Suspense 实验性。
* 现在也支持在 Vue 2.7 中使用,
** Vue 2.7 中支持,但仅用于类型推断。
三.新的框架级别推荐。
Vue 3 的支持库进行了重大更新。以下是新的默认建议的摘要:
1. 新版本的 Router, Devtools & test utils 来支持 Vue 3;
2. 构建工具链: Vue CLI -> Vite【/】
3. 状态管理: Vuex -> Pinia【/zh/】
4. IDE 支持: Vetur -> Volar【/items?itemName=】
5. 新的 TypeScript 命令行工具: vue-tsc【/vuejs/language-tools/tree/master/vue-language-tools/vue-tsc】
6. 静态网站生成: VuePress -> VitePress【/】
7. JSX: @vue/babel-preset-jsx -> @vue/babel-plugin-jsx【/vuejs/babel-plugin-jsx】
四.用于迁移的构建版本。
@vue/compat (即“迁移构建版本”) 是一个 Vue 3 的构建版本,提供了可配置的兼容 Vue 2 的行为。
该构建版本默认运行在 Vue 2 的模式下——大部分公有 API 的行为和 Vue 2 一致,仅有一小部分例外。使用在 Vue 3 中发生改变或被废弃的特性时会抛出运行时警告。一个特性的兼容性也可以基于单个组件进行开启或禁用。
预期用例:
1. 将一个 Vue 2 应用升级为 Vue 3 (存在限制)。
2. 迁移一个库以支持 Vue 3。
3. 对于尚未尝试 Vue 3 的资深 Vue 2 开发者来说,迁移构建版本可以用来代替 Vue 3 以更好地学习版本之间的差异。
五.非兼容性改变。
参考文档:/zh/breaking-changes/
1. 全局API。
(1). 全局 Vue API 更改为使用应用程序实例。
Vue 有许多全局 API 和配置,它们可以全局改变 Vue 的行为。例如,要注册全局组件,可以使用 API,虽然这种声明方式很方便,但它也会导致一些问题。从技术上讲,Vue 2 没有“app”的概念,Vue 2定义的应用只是通过 new Vue() 创建的根 Vue 实例。从同一个 Vue 构造函数创建的每个根实例共享相同的全局配置。
createApp是 Vue 3 中的 一个 新概念,调用 createApp 返回一个应用实例。
import { createApp } from 'vue'
const app = createApp({})
如果使用的是 Vue3 的 CDN 构建版本,那么 createApp 将通过全局的 Vue 对象暴露。
const { createApp } = Vue
const app = createApp({})
(2). 全局和内部 API 都经过了重构,现已支持 TreeShaking (摇树优化)。
由于之前的 Vue2 版本中的代码编写方式,如 () 这样的全局 API 是不支持 tree-shake 的,不管它们实际上是否被使用了,都会被包含在最终的打包产物中。
在 Vue 3 中,全局和内部 API 都经过了重构,并考虑到了 tree-shaking 的支持。因此,对于 ES 模块构建版本来说,全局 API 现在通过具名导出进行访问。例如:
import { nextTick } from 'vue'
nextTick(() => {
// 一些和 DOM 有关的东西
})
2. 模板指令。
(1). v-model 指令在组件上的使用已经被重新设计,替换掉了 。
用于自定义组件时,v-model prop 和事件默认名称已更改:
prop:value -> modelValue;
事件:input -> update:modelValue;
v-bind 的 .sync 修饰符和组件的 model 选项已移除,可在 v-model 上加一个参数.lazy代替。
现在可以在同一个组件上使用多个 v-model 绑定,还可以自定义 v-model 修饰符。
(2). 在<template v-for> 和没有 v-for 的节点身上使用 key 发生了变化。
对于 v-if/v-else/v-else-if 的各分支项 key 将不再是必须的,因为现在 Vue 会自动生成唯一的 key。
如果你手动提供 key,那么每个分支必须使用唯一的 key。你将不再能通过故意使用相同的 key 来强制重用分支。
<template v-for> 的 key 应该设置在 <template> 标签上 (而不是设置在它的子节点上)。
(3). v-if 和 v-for 在同一个元素身上使用时的优先级发生了变化。
版本中,在一个元素上同时使用 v-if 和 v-for 时,v-for 会优先作用。
版本中 v-if 总是优先于 v-for 生效。
(4). v-bind="object" 现在是顺序敏感的。
在 中,如果一个元素同时定义了 v-bind="object" 和一个相同的独立 attribute,那么这个独立 attribute 总是会覆盖 object 中的绑定。
示例:
<!-- 模板 -->
<div v-bind="{ id: 'blue' }"></div>
<!-- 结果 -->
<div ></div>
在 中,如果一个元素同时定义了 v-bind="object" 和一个相同的独立 attribute,那么绑定的声明顺序将决定它们如何被合并【后面覆盖前面】。
(5). v-on: 事件修饰符已经被移除。
Vue3中 新增的 emits 选项允许子组件定义真正会被触发的事件。
3. 组件。
(1). 函数式组件只能通过纯函数进行创建。
中函数式组件带来的性能提升在 中已经可以忽略不计,因此建议只使用有状态的组件。
中函数式组件只能由接收 props 和 context (即:slots、attrs、emit) 的普通函数创建。
(2). 单文件组件 (SFC) <template> 标签的 functional attribute 和函数式组件的 functional 选项已被移除。
(3). 异步组件现在需要通过 defineAsyncComponent 方法进行创建。
新的 defineAsyncComponent 助手方法,用于显式地定义异步组件。
component 选项被重命名为 loader。
Loader 函数本身不再接收 resolve 和 reject 参数,且必须返回一个 Promise。
Vue2异步组件是通过将组件定义为返回 Promise 的函数来创建的,例如:
const asyncModal = () => import('./')
在 Vue 3 中,由于函数式组件被定义为纯函数,因此异步组件需要通过将其包裹在新的 defineAsyncComponent 助手方法中来显式地定义。
示例:
import { defineAsyncComponent } from 'vue'
import ErrorComponent from './components/'
import LoadingComponent from './components/'
// 不带选项的异步组件
const asyncModal = defineAsyncComponent(() => import('./'))
// 带选项的异步组件
const asyncModalWithOptions = defineAsyncComponent({
loader: () => import('./'),
delay: 200,
timeout: 3000,
errorComponent: ErrorComponent,
loadingComponent: LoadingComponent
})
(4). 组件事件现在应该使用 emits 选项进行声明。
Vue 3 现在提供一个 emits 选项,和现有的 props 选项类似。这个选项可以用来定义一个组件可以向其父组件触发的事件。
Vue3中 emits选项会影响一个 监听器 被解析为 组件事件监听器 还是 原生DOM事件监听器,注意原生DOM事件存在冒泡触发机制【可通过在emits选项中添加对应的事件名避免被解析为原生DOM事件监听器】。
4. 渲染函数。
(1). 渲染函数 API 更改【此更改不会影响 <template> 用户】。
h 现在是需要从全局导入,而不是默认可作为参数传递给渲染函数。
更改了渲染函数参数,使其在有状态组件和函数组件的表现更加一致。
VNode 现在有一个扁平的 prop 结构。
示例:
// 语法
{
class: ['button', { 'is-outlined': isOutlined }],
style: [{ color: '#34495E' }, { backgroundColor: buttonColor }],
id: 'submit',
innerHTML: '',
onClick: submitForm,
key: 'submit-button'
}
(2). $scopedSlots property 已移除,所有插槽都通过 $slots 作为函数暴露。
示例一:
// Syntax
h(LayoutComponent, {}, {
header: () => h('div', ),
content: () => h('div', )
})
示例二:
//需要以编程方式引用作用域插槽时。
// 语法
this.$
// 语法
this.$()
(3). $listeners 被移除或整合到 $attrs。
$listeners 对象在 Vue 3 中已被移除。事件监听器现在是 $attrs 的一部分。
在 Vue 3 的虚拟 DOM 中,事件监听器现在只是以 on 为前缀的 attribute,这样它就成为了 $attrs 对象的一部分,因此 $listeners 被移除了。
示例:
{
text: '这是一个 attribute',
onClose: () => ('close 事件被触发')
}
(4). $attrs 现在包含 class 和 style attribute。
5. 自定义元素。
(1). 自定义元素检测现在在模板编译时执行。
检测并确定哪些标签应该被视为自定义元素的过程,现在会在模板编译期间执行,且应该通过编译器选项而不是运行时配置来配置。
(2). 特殊的 is attribute 的使用被严格限制在保留的 <component> 标签中。
为了支持 在原生元素上使用 is 的用例来处理原生 HTML 解析限制,Vue3 用 vue: 前缀来解析一个 Vue 组件。
示例:
<table>
<tr is="vue:blog-post-row"></tr>
</table>
6. 其它小改变。
(1). beforeDestroy 生命周期选项被重命名为 beforeUnmount,destroyed 生命周期选项被重命名为 unmounted。
(2). Vue3中 选项式 生命周期选项 相比较 Vue2生命周期钩子 增加了 renderTracked【开发调试用,在一个响应式依赖被组件的渲染作用追踪后调用。】、renderTriggered【开发调试用,在一个响应式依赖被组件触发了重新渲染之后调用。】、serverPrefetch【SSR选项,在 组件实例在服务器上被渲染 之前调用】 三个选项。
(3). 生成 prop 默认值的工厂函数不再能访问 this。
Vue3组件接收到的原始 prop 将作为参数传递给默认函数,inject API 可以在默认函数中使用。
示例:
import { inject } from 'vue'
export default {
props: {
theme: {
default (props) {
// `props` 是传递给组件的、
// 在任何类型/默认强制转换之前的原始值,
// 也可以使用 `inject` 来访问注入的 property
return inject('theme', 'default-theme')
}
}
}
}
(4). 自定义指令的 API 已更改为与组件生命周期一致,且 已移除。
Vue3自定义指令的钩子函数已经被重命名,以更好地与组件的生命周期保持一致。
示例:
const MyDirective = {
created(el, binding, vnode, prevVnode) {}, // 新增
beforeMount() {},
mounted() {},
beforeUpdate() {}, // 新增
updated() {},
beforeUnmount() {}, // 新增
unmounted() {}
}
(5). data 选项应始终被声明为一个函数。
组件选项 data 的声明不再接收纯 JavaScript object,而是接收一个 function。
(6). 来自 mixin 的 data 选项现在为浅合并。
当合并来自 mixin 或 extend 的多个 data 返回值时,合并操作现在是浅层次的而非深层次的 (只合并根级属性)。
(7). Attribute 强制策略已更改。
移除 枚举attribute【包括 contenteditable、draggable 和 spellcheck】 的内部概念,并将这些 attribute 视为普通的非布尔 attribute。
如果值为布尔值 false,则不再移除 attribute。取而代之的是,它将被设置为 attr="false"。若要移除 attribute,应该使用 null 或者 undefined。
(8). Transition 的一些 class 被重命名。
过渡类名 v-enter 修改为 v-enter-from、过渡类名 v-leave 修改为 v-leave-from。
(9). <TransitionGroup> 不再默认渲染包裹元素。
<transition-group> 不再默认渲染根元素,但仍然可以用 tag attribute 创建根元素。
(10). 当侦听一个数组时,只有当数组被替换时,回调才会触发,如果需要在变更时触发,则必须指定 deep 选项。
换句话说,在数组被改变时侦听回调将不再被触发。要想在数组被改变时触发侦听回调,必须指定 deep 选项。
(11). 没有特殊指令的标记 (v-if/else-if/else、v-for 或 v-slot) 的 <template> 现在被视为普通元素,并将渲染为原生的 <template> 元素,而不是渲染其内部内容。
(12). 已挂载的应用不会替换它所挂载的元素,而是会作为子元素插入。
在 Vue 中,当挂载一个具有 template 的应用时,被渲染的内容会替换我们要挂载的目标元素。
在 Vue 中,被渲染的应用会作为子元素插入,从而替换目标元素的 innerHTML。
(13). 生命周期的 hook: 事件前缀改为 vue:。
在 Vue 2 中,我们可以通过事件来监听组件生命周期中的关键阶段。这些事件名都是以 hook: 前缀开头,并跟随相应的生命周期钩子的名字。
在 Vue 3 中,这个前缀已被更改为 vue:。额外地,这些事件现在也可用于 HTML 元素,和在组件上的用法一样。
示例一:
<template>
<child-component @vue:updated="onUpdated">
</template>
示例二:
<template>
<input @vue:mounted="({ el }) => ()">
</template>
(14). 对于组件间的逻辑复用,推荐使用组合式 API 的组合式函数,而不是mixins。
(15). Vue3中,number 修饰符会在输入框有 type="number" 时自动启用。而Vue2中不会。
7. 被移除的 API。
(1). Vue3不再支持使用数字 (即键码) 作为 v-on 修饰符,不再支持 。
从 已被废弃开始,Vue 3 继续支持这一点就不再有意义了。因此,现在建议对任何要用作修饰符的键使用 kebab-cased (短横线) 名称。
示例:
<!-- Vue 3 在 v-on 上使用按键修饰符 -->
<input v-on:-down="nextPage">
<!-- 同时匹配 q 和 Q -->
<input v-on:="quit">
(2). $on,$off 和 $once 实例方法已被移除,组件实例不再实现事件触发接口。
(3). 从 Vue 3.0 开始,过滤器 (filter)已移除,且不再支持。
(4). 对 内联模板 attribute【inline-template】的支持已被移除。
(5). Vue 3.0 中 $children 实例 property 已移除,不再支持。
(6). propsData 选项之前用于在创建 Vue 实例的过程中传入 prop,现在它被移除了。如果想为 Vue 3 应用的根组件传入 prop,请使用 createApp 的第二个参数。
(7). 已移除 $destroy 实例方法。用户不应该再手动管理单个 Vue 组件的生命周期。
(8). 已移除 全局函数 set 和 delete 以及实例方法 $set 和 $delete。基于代理的变化检测已经不再需要它们了。