Appearance
用于迁移的构建版本
概述
@vue/compat (即“迁移构建版本”) 是一个 Vue 3 的构建版本,提供了可配置的兼容 Vue 2 的行为。
该构建版本默认运行在 Vue 2 的模式下——大部分公有 API 的行为和 Vue 2 一致,仅有一小部分例外。使用在 Vue 3 中发生改变或被废弃的特性时会抛出运行时警告。一个特性的兼容性也可以基于单个组件进行开启或禁用。
预期用例
- 将一个 Vue 2 应用升级为 Vue 3 (存在限制)
- 迁移一个库以支持 Vue 3
- 对于尚未尝试 Vue 3 的资深 Vue 2 开发者来说,迁移构建版本可以用来代替 Vue 3 以更好地学习版本之间的差异。
已知的限制
虽然我们已经努力使迁移构建版本尽可能地模拟 Vue 2 的行为,但仍有一些限制可能会阻止应用的顺利升级:
基于 Vue 2 内部 API 或文档中未记载行为的依赖。最常见的情况就是使用
VNodes上的私有 property。如果你的项目依赖诸如 Vuetify、Quasar 或 Element UI 等组件库,那么最好等待一下它们的 Vue 3 兼容版本。对 IE11 的支持:Vue 3 已经官方放弃对 IE11 的支持。如果仍然需要支持 IE11 或更低版本,那你仍需继续使用 Vue 2。
服务端渲染:该迁移构建版本可以被用于服务端渲染,但是迁移一个自定义的服务端渲染配置还有很多工作要做。大致的思路是将
vue-server-renderer替换为@vue/server-renderer。Vue 3 不再提供一个包渲染器,且我们推荐使用 Vite 以支持 Vue 3 服务端渲染。如果你正在使用 Nuxt.js,可以尝试 Nuxt Bridge,一个 Nuxt.js 2 到 3 的兼容层。对于复杂、生产环境的项目来说,可能更好还是等待一下 Nuxt 3。
预期
请注意迁移构建版本旨在覆盖在文档中公开记载的 Vue 2 API 和行为。如果应用依赖了未记载的行为导致在迁移构建下运行失败,我们可能不太会调整迁移构建版本以迎合这种特殊情况。请考虑重构或移除导致这些问题行为的依赖。
多留意一下:如果你的应用较大且复杂,即便有了迁移构建版本,整个迁移过程也会是一个挑战。如果你的应用不幸无法顺利升级,请留意我们正在计划将组合式 API 等其它 Vue 3 特性迁移回 Vue 2.7 (预计在 2021 年第三季度末)。
如果应用在迁移构建版本中顺利运行,你可以在迁移完成之前将其发布到生产环境。尽管存在一些小的性能或包大小的问题,但应该不会显著地影响到生产环境的用户体验。当你有基于 Vue 2 行为的依赖且无法升级/替换时,可能不得不这样做。
该迁移构建版本会从 3.1 开始提供,且会随着 3.2 的发布计划进行持续发布。我们计划在将来某个小版本号起最终停止发布迁移构建版本 (在 2021 年底前至少不会),因此你仍需要在此之前将其迁移到标准构建版本。
升级流程
下面的工作流程讲述了将一个实际的 Vue 2 应用 (Vue HackerNews 2.0) 迁移到 Vue 3 的过程。完整的提交记录在这里。请注意,你的项目所需要的实际步骤可能有所不同。下面的步骤仅应被视为一般性的指南,而非严格的教程。
准备工作
- 如果你仍然使用废弃的具名/作用域插槽语法,请先将其更新至 (2.6 已经支持的) 最新的语法。
安装
尽可能升级工具。
- 如果使用了自定义的 webpack 设置:将
vue-loader升级至^16.0.0。 - 如果使用了
vue-cli:通过vue upgrade升级到最新的@vue/cli-service。 - (替代方案) 迁移至 Vite + vite-plugin-vue2。[示例提交]
- 如果使用了自定义的 webpack 设置:将
在
package.json里,将vue更新到 3.1,安装相同版本的@vue/compat。且如果存在vue-template-compiler的话,将其替换为@vue/compiler-sfc。diff"dependencies": { - "vue": "^2.6.12", + "vue": "^3.1.0", + "@vue/compat": "^3.1.0" ... }, "devDependencies": { - "vue-template-compiler": "^2.6.12" + "@vue/compiler-sfc": "^3.1.0" }在构建设置中,为
vue设置别名@vue/compat,且通过 Vue 编译器选项开启兼容模式。示例配置
vue-cli
js// vue.config.js module.exports = { chainWebpack: (config) => { config.resolve.alias.set('vue', '@vue/compat') config.module .rule('vue') .use('vue-loader') .tap((options) => { return { ...options, compilerOptions: { compatConfig: { MODE: 2 } } } }) } }普通 webpack
js// webpack.config.js module.exports = { resolve: { alias: { vue: '@vue/compat' } }, module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { compilerOptions: { compatConfig: { MODE: 2 } } } } ] } }Vite
js// vite.config.js export default { resolve: { alias: { vue: '@vue/compat' } }, plugins: [ vue({ template: { compilerOptions: { compatConfig: { MODE: 2 } } } }) ] }如果使用了 TypeScript,你还需要修改
vue的类型,通过添加一个*.d.ts暴露其 (在 Vue 3 中已经不再展示) 默认导出。tsdeclare module 'vue' { import { CompatVue } from '@vue/runtime-dom' const Vue: CompatVue export default Vue export * from '@vue/runtime-dom' const { configureCompat } = Vue export { configureCompat } }在此,你的应用可能会遇到一些编译时的错误/警告 (例如对过滤器的使用),请先修复这些错误。直至所有的编译警告都消失,当然,你也可以把编译器设置为 Vue 3 模式。
在修复了这些错误之后,如果没有受制于上述的限制,那么应用就应该可以运行了。
你可能会同时从命令行和浏览器控制台看到很多警告。这里提供一些一般化的小建议:
你可以在浏览器控制台里过滤特定的警告。建议通过使用过滤器,使自己每次专注修复同一种问题。你也可以使用类似
-GLOBAL_MOUNT的否定式过滤器。你可以通过兼容性配置关闭对特定的废弃内容的处理。
有些警告可能来自你使用的依赖 (如
vue-router)。你可以通过警告的组件嵌套或调用栈的追踪信息 (可以点击展开) 来进行检查。可以优先专注于修复源自你自己代码的警告。如果你使用了
vue-router,请注意在升级至vue-routerv4 之前,<transition>和<keep-alive>无法和<router-view>一起工作。
升级
<transition>类名。这是唯一没有运行时警告的特性。你可以在整个项目范围内做一次.*-enter和.*-leaveCSS 类名的搜索。更新应用的入口以使用新的全局挂载 API。
将
vue-router升级至 v4。如果你还使用了vuex-router-sync,可以同时将其替换为一个 store getter。升级过后,同
<router-view>一起使用<transition>和<keep-alive>就要求使用新的基于作用域插槽的语法。逐个修复警告。注意有些特性在 Vue 2 和 Vue 3 之间存在行为冲突——例如渲染函数 API,或函数式组件 vs. 异步组件的改变。为了迁移到 Vue 3 API 而不影响到应用的其它部分,你可以通过
compatConfig选项对单个组件选择性启用 Vue 3 的行为。当修复了所有警告以后,你就可以移除迁移构建版本并切换为 Vue 3。注意如果存在基于 Vue 2 行为的依赖,你可能无法做到这一点。
兼容性配置
全局配置
兼容性特性可以进行单独禁用:
js
import { configureCompat } from 'vue'
// 禁用某些兼容性特性
configureCompat({
FEATURE_ID_A: false,
FEATURE_ID_B: false
})或者整个应用默认为 Vue 3 的行为,仅开启某些兼容性特性:
js
import { configureCompat } from 'vue'
// 所有 Vue 3 的默认行为,并开启某些兼容性特性
configureCompat({
MODE: 3,
FEATURE_ID_A: true,
FEATURE_ID_B: true
})基于单个组件的配置
一个组件可以使用 compatConfig 选项,并支持与全局 configureCompat 方法相同的选项:
js
export default {
compatConfig: {
MODE: 3, // 只为这个组件选择性启用 Vue 3 行为
FEATURE_ID_A: true // 也可以在组件级别开启某些特性
}
// ...
}针对编译器的配置
以 COMPILER_ 开头的特性是针对编译器的:如果你正在使用完整构建版本 (含浏览器内编译器),它们可以在运行时中被配置。然而如果使用构建设置,它们必须换为通过在构建配置中的 compilerOptions 进行配置 (参阅上述的配置)。
特性参考
兼容性类型
- ✔ 完全兼容
- ◐ 部分兼容且附带注意事项
- ⨂ 不兼容 (只有警告)
- ⭘ 仅兼容 (没有警告)
不兼容
应该被修复,否则很可能会导致错误
| ID | 类型 | 描述 | 文档 |
|---|---|---|---|
| GLOBAL_MOUNT_CONTAINER | ⨂ | 被挂载的应用不会替换被挂载到的元素 | 链接 |
| CONFIG_DEVTOOLS | ⨂ | 生产环境开发者工具现在是一个构建时的开关 | 链接 |
| COMPILER_V_IF_V_FOR_PRECEDENCE | ⨂ | v-if 和 v-for 用在相同的元素上时的处理顺序发生了改变 | 链接 |
| COMPILER_V_IF_SAME_KEY | ⨂ | v-if 分支不能再拥有相同的 key | 链接 |
| COMPILER_V_FOR_TEMPLATE_KEY_PLACEMENT | ⨂ | <template v-for> key 现在应该被放在 <template> 上 | 链接 |
| COMPILER_SFC_FUNCTIONAL | ⨂ | 单文件组件中不再支持 <template functional> | 链接 |
部分兼容且附带注意事项
| ID | 类型 | 描述 | 文档 |
|---|---|---|---|
| CONFIG_IGNORED_ELEMENTS | ◐ | config.ignoredElements 现在改为了 config.compilerOptions.isCustomElement (只在浏览器编译器构建版本中)。如果使用了构建设置,isCustomElement 必须通过构建配置传入。 | 链接 |
| COMPILER_INLINE_TEMPLATE | ◐ | inline-template 被移除 (兼容模式只在浏览器编译器构建版本中支持) | 链接 |
| PROPS_DEFAULT_THIS | ◐ | prop 的默认工厂方法不再可以访问 this (在兼容模式下,this 不是一个真实的实例——它只暴露 prop、$options 和注入) | 链接 |
| INSTANCE_DESTROY | ◐ | $destroy 实例方法被移除 (在兼容模式下,只在根实例下支持) | |
| GLOBAL_PRIVATE_UTIL | ◐ | Vue.util 是私有的,且不再可用 | |
| CONFIG_PRODUCTION_TIP | ◐ | 不再需要 config.productionTip | 链接 |
| CONFIG_SILENT | ◐ | config.silent 被移除 |
仅兼容 (无告警)
| ID | 类型 | 描述 | 文档 |
|---|---|---|---|
| TRANSITION_CLASSES | ⭘ | 过渡动画的进入/离开的 class 发生了变化 | 链接 |
完全兼容
| ID | 类型 | 描述 | 文档 |
|---|---|---|---|
| GLOBAL_MOUNT | ✔ | new Vue() -> createApp | 链接 |
| GLOBAL_EXTEND | ✔ | Vue.extend 被移除 (使用 defineComponent 或 extends 选项) | 链接 |
| GLOBAL_PROTOTYPE | ✔ | Vue.prototype -> app.config.globalProperties | 链接 |
| GLOBAL_SET | ✔ | Vue.set 被移除 (不再需要) | |
| GLOBAL_DELETE | ✔ | Vue.delete 被移除 (不再需要) | |
| GLOBAL_OBSERVABLE | ✔ | Vue.observable 被移除 (使用 reactive) | 链接 |
| CONFIG_KEY_CODES | ✔ | config.keyCodes 被移除 | 链接 |
| CONFIG_WHITESPACE | ✔ | 在 Vue 3 中空格默认为 "condense" | |
| INSTANCE_SET | ✔ | vm.$set 被移除 (不再需要) | |
| INSTANCE_DELETE | ✔ | vm.$delete 被移除 (不再需要) | |
| INSTANCE_EVENT_EMITTER | ✔ | vm.$on、vm.$off、vm.$once 被移除 | 链接 |
| INSTANCE_EVENT_HOOKS | ✔ | 实例不再抛出 hook:x 事件 | 链接 |
| INSTANCE_CHILDREN | ✔ | vm.$children 被移除 | 链接 |
| INSTANCE_LISTENERS | ✔ | vm.$listeners 被移除 | 链接 |
| INSTANCE_SCOPED_SLOTS | ✔ | vm.$scopedSlots 被移除;vm.$slots 现在暴露函数 | 链接 |
| INSTANCE_ATTRS_CLASS_STYLE | ✔ | $attrs 现在包含了 class 和 style | 链接 |
| OPTIONS_DATA_FN | ✔ | data 在所有情况下都必须是一个函数 | 链接 |
| OPTIONS_DATA_MERGE | ✔ | 来自 mixin 或扩展的 data 现在都是浅合并 | 链接 |
| OPTIONS_BEFORE_DESTROY | ✔ | beforeDestroy -> beforeUnmount | |
| OPTIONS_DESTROYED | ✔ | destroyed -> unmounted | |
| WATCH_ARRAY | ✔ | 对于一个数组的操作,侦听无法被触发了,除非使用了深度侦听 | 链接 |
| V_ON_KEYCODE_MODIFIER | ✔ | v-on 不再支持 keyCode 修饰符 | 链接 |
| CUSTOM_DIR | ✔ | 自定义指令钩子命名变化 | 链接 |
| ATTR_FALSE_VALUE | ✔ | attribute 的绑定值为布尔值 false 时不再将其移除 | 链接 |
| ATTR_ENUMERATED_COERCION | ✔ | 不再特殊处理枚举类型 attribute | 链接 |
| TRANSITION_GROUP_ROOT | ✔ | <transition-group> 不再默认渲染一个根元素 | 链接 |
| COMPONENT_ASYNC | ✔ | 异步组件 API 改变 (现在需要 defineAsyncComponent) | 链接 |
| COMPONENT_FUNCTIONAL | ✔ | 函数式组件 API 改变 (现在必须只是一个普通函数) | 链接 |
| COMPONENT_V_MODEL | ✔ | 组件的 v-model 修改 | 链接 |
| RENDER_FUNCTION | ✔ | 渲染函数 API 更改 | 链接 |
| FILTERS | ✔ | 过滤器被移除 (该选项只会影响运行时的过滤器 API) | 链接 |
| COMPILER_IS_ON_ELEMENT | ✔ | is 的使用现在被严格限制在 <component> 上 | 链接 |
| COMPILER_V_BIND_SYNC | ✔ | v-bind.sync 被替换为带参数的 v-model | 链接 |
| COMPILER_V_BIND_OBJECT_ORDER | ✔ | v-bind="object" 现在是顺序敏感的 | 链接 |
| COMPILER_V_ON_NATIVE | ✔ | v-on.native 修饰符被移除 | 链接 |
| COMPILER_V_FOR_REF | ✔ | v-for 中的 ref (编译器支持)) | |
| COMPILER_NATIVE_TEMPLATE | ✔ | 没有特殊指令的 <template> 现在会被渲染为原生元素 | |
| COMPILER_FILTERS | ✔ | 过滤器 (编译器支持) |