大家好,这里是大家的林语冰。欢迎关注"前端俱乐部"。
地球人都知道,Vue 2 的活跃支持在 2022 年 3 月 18 日结束,且在 2023 年 12 月 31 日结束生命周期。
这意味着,2024 开始,Vue 2 不再提供错误修复或安全补丁。如果您的应用仍在 Vue 2 上运行,那就必须时刻准备着,升级到 Vue 3。
Vue 团队一直为从 Vue 2 升级 Vue 3 的开发者未雨绸缪,提供了一个名为"迁移构建"的强大工具。该版本可作为名为 vue/compat
的 JS 包使用,专门设计来辅助团队从 Vue 2 升级到 Vue 3。
虽然但是,粉丝请注意,Vue 团队计划在未来停止发布迁移版本。
免责声明
本文属于是语冰的直男翻译了属于是,略有删改,仅供粉丝参考,英文原味版请传送 10 Steps to Migrate from Vue 2 to Vue 3。
本文受众
在本文中,我们共享从 Vue 2 顺利迁移到 Vue 3 的 10 个步骤。
如果您的应用需要支持 IE11(Internet Explorer 11),那意味着,您需要继续使用 Vue 2。
Vue 3 利用了与 IE11 旧版浏览器不兼容的现代 JS 功能和优化。因此,如果 IE11 兼容性是您的项目刚需,那就继续使用 Vue 2。
粉丝请注意,vue/compat
能且仅能支持公开暴露的 Vue API。在升级到 Vue 3 之前,我们必须识别和更新使用私有 API 的任何代码,这是一个额外注意事项。
私有 API 在不同的 Vue 版本中可能会变更或移除,因此必须检查代码库是否有依赖这些 API,并进行相应修改,确保与 Vue 3 的兼容性。
我们假设您打算升级的应用当前运行在 Vue 2.6/2.7 上,它们已经支持新型插槽语法。如果您的应用仍位于更旧版本的 Vue 上,建议首先升级到 Vue 2.7 或包含新型插槽语法更新的兼容版本。
最后,重要的是要考虑到我们的应用可能具有与 Vue 3 不兼容的依赖。本文将主要讨论更新两个关键依赖的必要性:Vuex 和 Vue Router。虽然但是,您的项目中可能还有其他依赖,也需要更新才能与 Vue 3 "梦幻联动"。
策略
对于升级策略,我们将首先在版本控制系统中创建一个新的迁移分支。我们会经常在主分支上重新调整迁移分支。变基或合并可确保我们将主分支的最新更改合并到迁移分支,使我们能够在执行迁移时使用应用的最新版本。
当我们继续迁移时,我们可以灵活地决定是在完成整个迁移之前,还是仅在完全切换到 Vue 3 之后,将迁移分支合并到主分支:
- 如果我们选择在完成整个迁移之前,将迁移分支合并到主分支,那么可以增量推送 Vue 3 更改。此方案可以辅助分配工作量,并减轻与"大爆炸迁移"相关的风险。
- 我们也可以决定仅在完成到 Vue 3 的整个迁移后,才将迁移分支合并到主分支。此方案可确保 Vue 2 和 Vue 3 代码库之间更清晰的分离。
无论哪种策略,充分的质量保证测试对于确保应用程序在生产环境中如期工作至关重要。这包括功能测试、回归测试和潜在的性能测试,可以识别和解决迁移过程中可能出现的任何问题或回归。
步骤 0:更新插槽语法
在从 Vue 2 升级到 Vue 3 之前,如果我们还在使用已废弃的命名/作用域插槽语法,那就必须更新我们应用中的插槽语法。
我们必须采用 Vue 2.6 及更高版本中已支持的最新语法。
Vue 2 旧语法(已弃用):
Vue 3 新语法:
步骤 1:更新 Vue 并添加 Vue-Compat
版本升级的第一步是将 Vue 更新到所需版本,并添加 Vue-Compat 包,建议两个包使用相同版本以确保兼容性。
此外,我们可以移除 vue-template-compiler
包,因为从 Vue 3.2.13 开始,vue/compiler-sfc
和 vue
的主包一起打包。这意味着,不再需要单独安装 vue/compiler-sfc
。
如下所示:
diff
"dependencies": {
- "vue": "^2.7.14",
+ "vue": "^3.3.4",
+ "@vue/compat": "^3.3.4"
...
},
"devDependencies": {
- "vue-template-compiler": "^2.7.14"
}
在此阶段,我们的应用可能会崩溃,测试可能会失败。但请不要害怕,因为在应用程序再次顺利运行之前,还有采取其他步骤。
步骤 2:将 vue
别名改为 @vue/compat
安装 @vue/compat
作为迁移版本后,下一步是更新您的 Webpack、Rollup、Vue-cli 或 Vite 配置。
这样做的目的是在将构建工具 vue
的别名配置为 @vue/compat
,这样每当引用 vue
时,它都会正确加载 @vue/compat
。
举个栗子,以下是使用 Webpack 的应用程序的具体注意事项,但其他构建工具也可以举一反一。
我们需要将 vue-loader
升级到版本 ^16.0.0
。要使用 yarn 更新 vue-loader
,我们可以运行:
bash
yarn upgrade vue-loader@^16.0.0
如果我们无法更新 vue-loader
,可能会导致如下的编译时错误。
我们可以确认 package.json
文件中的内容,确认更新是否成功,vue-loader
需要 webpack 4.1 及更高版本才能奏效。
diff
+ "vue-loader": "^16.0.0"
+ "webpack": "^4.1.0 || ^5.0.0-0"
在我们的 Webpack 配置文件中,可能有类似这样的代码,要更新它以进行 Vue 3 迁移,我们要对其进行修改:
diff
- 'vue$': 'vue/dist/vue.esm.js',
+ 'vue$': '@vue/compat/dist/vue.esm-bundler.js',
$
符号是 Webpack 使用的约定,用于在使用 vue
别名时指示完全匹配。此修改确保当在不同模块的导入路径中使用 vue
时,Webpack 可以正确地将其解析为 @vue/compat
。
步骤 3:更新过渡类名
为了兼容 Vue 3,我们需要更新应用中的 CSS 过渡类名(transition class names),比如向与过渡相关的现有 CSS 类名添加 -from
后缀。
我们可以在项目中搜索以 -enter
和 -leave
结尾的 CSS 类名,针对所有过渡类名添加 -from
后缀。通常,这些更改会在 CSS 文件中进行。
举个栗子,.fade-enter
修改为 .fade-enter-from
。
diff
- .fade-enter, .fade-leave-active {
+ .fade-enter-from, .fade-leave-active {
步骤 4:修复编译时问题
根据 Vue-Compat 迁移指南,在 Vue 2 和 Vue 3 之间 6 种已知的不兼容性,可能会导致编译时错误。
我们需要预先修复这些不兼容性,确保顺利升级。
问题 1:挂载的 App 不会替换其挂载的元素
检查渲染的 DOM,并密切注意已挂载的 Vue App 以及父容器的 ID。
如果您发现 ID 相同,那么您需要更新两个 ID 之一,以及对该 DOM 节点的 JS 或 CSS 引用。
Vue 2 旧写法:
Vue 3 新写法:
问题 2:生产开发工具现在是构建时标志
运行 App 时,您可能会看到以下错误消息。
md
Feature flags **VUE_OPTIONS_API**, **VUE_PROD_DEVTOOLS** are not explicitly defined. You are
running the esm-bundler build of Vue, which expects these compile-time feature flags to be
globally injected via the bundler config in order to get better tree-shaking in the production
bundle.
For more details, see https://link.vuejs.org/feature-flags.
简而言之,就是建议我们在构建工具中正确配置构建时标志,确保我们的最终构建包有效。
如果您使用 Webpack,更改可能如下所示:
js
const webpack = require('webpack')
module.exports = {
plugins: [
// 定义打包构建功能标志
new webpack.DefinePlugin({
__VUE_OPTIONS_API__: true, // 启用/禁用选项式 API 支持
__VUE_PROD_DEVTOOLS__: false // 启用/禁用生产环境的开发工具支持
})
]
}
问题 3:同一元素上使用 v-if
和 v-for
的优先级已更改
在 Vue 3 中,在同一元素上使用 v-if
和 v-for
指令时,其优先级已更改。
一个简单的解决方案是,将 v-for
元素包装在 <template>
元素内。然后,我们可以将 v-if
指令移动到新的 <template>
元素。
Vue 2 旧写法:
Vue 3 新写法:
问题 4:v-if
分支不能再具有相同的 key
在 Vue 3 中,禁止 v-if
分支拥有相同的 key
。遭遇此问题时,根据具体用例,有若干可用的选项:
- 删除
key
:某些情况下,我们可以从v-if
分支中完全删除key
属性。Vue 会根据其内部算法自动分配key
。当分支的顺序和存在稳定并且不依赖特定的key
时,此方法适用。删除key
可以简化代码,并让 Vue 为您分配key
。 - 分配唯一
key
:如果v-if
分支由于动态变化或特定需求而需要显式key
,那就需要确保每个分支都有唯一的key
。此方案保证了 Vue 可以区分分支,并执行精准更新。
问题 5:<template v-for>
的 key
现在应放在 <template>
上
在 Vue 3 中,将 <template>
与 v-for
一起使用时,key
属性的位置发生了变化。
与 Vue 2 中 key
属性放置在 <template>
内的内部元素上不同,在 Vue 3 中,我们应该将 key
直接添加到 <template>
元素本身。
Vue 2 旧写法:
Vue 3 新写法:
问题 6:SFC 不再支持 <template functional>
在 Vue 2 中,函数式组件提供了一种返回多个根节点的方法。虽然但是,在 Vue 3 中,Vue 团队决定移除函数式组件的语法。
要更新代码库,我们需要从 SFC(单文件组件)中删除 functional
属性,或者如果您将函数组件与渲染函数一起使用,那就需要删除 { functional: true }
选项。
步骤 6:修复运行时警告
解决编译时错误并成功运行应用程序后,在迁移过程中通常会遭遇大量警告。这些警告可能会出现在命令行输出和浏览器控制台中。
全局 API 变更
为了解决 Vue 3 中的全局 API 更改,特别是引入 createApp
以及删除 vm.$mount()
方法和 el
选项,我们需要更新应用代码。
请务必更新 App 中使用已弃用的全局实例 API 的所有相关部分,遵循新的 createApp
方法。
处理已删除的 API
Vue 3 不再支持按键代码事件修饰符。如果您的应用使用按键代码事件修饰符,旧需要将它们转换为等效的命名修饰符。
$on
、$off
和 $once
实例方法已在 Vue 3 中删除。如果您使用它们来实现全局事件总线,那就需要另辟蹊径。
一种方案是诉诸第三方库,比如 mitt
或 tiny-emitter
实现事件总线。
举个栗子,诉诸 tiny-emitter
替换 Vue 2 组件事件行为:
js
// eventBus.js
import emitter from 'tiny-emitter/instance'
export default {
$on: (...args) => emitter.on(...args),
$once: (...args) => emitter.once(...args),
$off: (...args) => emitter.off(...args),
$emit: (...args) => emitter.emit(...args)
}
// main.js
import eventBus from './eventBus.js'
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// 这是的 $bug 在此 App 的任意组件模板中可用,
// 同时在任意组件实例的 this 上也可用
app.config.globalProperties.$bus = eventBus
app.mount('#app')
在上面的 Vue 3 代码中,我们创建了一个事件总线。然后,我们使用 createApp
创建 Vue App 实例,并将 emitter
实例赋值给 $bus
全局属性。
js
export default {
mounted() {
this.$bus.$emit('some-event')
}
}
Vue 3 已删除过滤器。要实现同款功能,我们可以在组件中使用计算属性或方法。
使用计算属性而不是全局定义的过滤器可能会导致多余的代码重复。为了防止代码重复,我们可以使用 mixins
或组合式 API。
定义内联模板已在 Vue 3 中删除。您需要重构代码,使用单独的模板文件或组件模板的渲染函数。
$children
实例属性提供了对组件子组件的直接访问,已在 Vue 3 中删除。如果您的代码依赖此属性,那就需要使用其他技术重构它,比如组件引用或作用域插槽。
propsData
选项允许在实例化期间将 props
传递给根组件,该选项已在 Vue 3 中删除 。我们应该在使用 createApp
创建根组件时使用 props
。
渲染函数 API 的破坏性更新
如果您的应用程序使用渲染函数 API,其显著变化包括但不限于:
h
现已全局导入。- 渲染函数中的访问插槽千变万化。
$attrs
对象现在包含class
、style
和$listeners
等属性。此更改对严重依赖$attrs
和$listeners
的项目有影响。
步骤 7:升级 Vuex
迁移到 Vue 3 时,将 Vuex 3 升级到 Vuex 4 至关重要,因为 Vuex 3 与 Vue 3 不兼容。
幸运的是,从 Vuex 3 升级到 Vuex 4 的过程相对简单。大多数 API 保持不变,但在继续之前需要解决若干破坏性更新。
我们在安装步骤中不再将 Vuex 传递给 Vue,而是使用 app.use()
方法将 store
直接传递到 App:
js
// main.js
import { store } from './store'
import App from './App.vue'
const app = createApp(App)
app.use(store)
步骤 8:升级 Vue-Router
作为 Vue 3 迁移的一部分,我们有必要升级到 Vue-Router 4。这个新版本的 Vue-Router 引入了若干破坏性更新,我们需要注意这些更改才能成功迁移到 Vue 3。
举一反一,引入了 createRouter
函数,它取代了之前的 new Router()
用法。
步骤 9:更新 Typescript 配置
迁移到 Vue 3 后,检查和更新项目中的 TS 配置,确保兼容性并利用新功能至关重要。
Vue 3 引入了 defineComponent
包装函数,它通过改进的类型推断支持,增强组件创建。
为了确保顺利迁移,建议检查您的 TS 配置,并更新它以符合 Vue 3 要求和最佳实践。
步骤 10:Vue 3 启动
行文至此,我们系统地解决了 Vue 2 和 Vue 3 之间的冲突行为。我们还解决了由于编译时错误、已删除 API 和 Vue 3 中添加的新功能而导致的更改。
修复所有警告且代码库准备就绪后,我们可以删除迁移构建,并直接切换到 Vue 3。
虽然但是,粉丝请注意,如果您的项目具有仍然依赖 Vue 2 行为的依赖,那可能无法立即切换到 Vue 3。
要从"迁移构建"切换到官方 Vue 3 构建,我们需要更新构建工具中的 vue
别名。我们可以通过将以下行添加到 Webpack 配置中来更新 vue
别名:
js
'vue$': 'vue/dist/vue.esm-bundler.js',
我们可以利用 vue-compat
提供的 configureCompat
函数一次将 App 切换到一个组件或功能。
js
import { createApp, configureCompat } from 'vue'
configureCompat({})
完美谢幕
升级到 Vue 3 不仅仅是为了追逐 Vue 框架的最新版本;这还关乎拥抱更高效、更强大的开发体验。
借助 Vue 3,我们可以灵活使用组合式 API、改良的性能优化和强化的 TS 支持等新功能。
本期话题是 ------ 你更多使用 Vue 2 还是 Vue 3?欢迎在本文下方自由言论,文明共享。
长期关注"前端俱乐部",坚持阅读,自律打卡,每天一次,进步一点。谢谢大家的点赞,掰掰~