2.1 Vue2 与 Vue3 的适配差异


2.1 Vue2 与 Vue3 的适配差异

本节导读:上一章立好了目录公约,本章进入语言层。本节先教你看懂工程的身份,再对照两条语法线在生命周期、全局属性、组件模型上的差异,最后给出老项目渐进升级的可行顺序。读完后,网上的示例代码属于哪条线、能不能抄,你应当一眼可辨。

接手商城的老仓库,第一件事不是改代码,而是验明正身:这是 Vue2 工程还是 Vue3 工程?编译管线走 webpack 还是 vite?答案决定后面一切示例的参考价值。鉴定方法在 1.3 节给过三处依据,这里补上工程里的实际样子:manifest.json 的 vueVersion 字段写 "2" 还是 "3";依赖清单里是 @dcloudio/webpack-uni-pages-loader 还是 @dcloudio/vite-plugin-uni;页面组件里是 options 配置式还是 setup 写法。三处对上,身份即定。

图 2-1 两条语法线的并行与汇流

图 2-1 两条语法线的并行与汇流

生命周期:差异最密集的地带

uni-app 的生命周期分三层:应用级(App.vue 的 onLaunch、onShow、onHide)、页面级(onLoad、onShow、onReady、onUnload 等)、组件级(Vue 标准的 created、mounted 等)。两条语法线的差异集中在页面级与组件级的写法上:

// Vue2 写法:页面生命周期直接作为选项 export default { onLoad(query) { console.log('页面参数:', query.id); }, onShow() { this.refreshBadge(); } };
// Vue3 组合式写法:页面生命周期从统一入口按需导入 import { onLoad, onShow, onUnload } from '@dcloudio/uni-app'; import { ref } from 'vue'; export default { setup() { const badge = ref(0); onLoad((query) => { console.log('页面参数:', query.id); }); onShow(() => { badge.value += 1; }); onUnload(() => { console.log('清理定时器等资源'); }); return { badge }; } };

两条铁律值得刻在脑子里:onLoad 只存在于页面,组件里注册不生效页面显示刷新类逻辑放 onShow 而不是 onLoad,因为从别的页面返回时 onLoad 不会再触发。漏了第二条,就会出现"第一次进来有数据、返回再进不刷新"的经典故障。

其余高发差异速查

差异点 Vue2 表现 Vue3 表现
全局属性挂载 Vue.prototype 上挂 app.config.globalProperties 上挂
过滤器 管道语法可用 已移除,改方法或计算属性
事件总线 常用实例总线通信 移除,改用 mitt 或 Pinia
多根节点 模板仅一个根 支持多根,注意属性继承行为
v-model 参数 model 选项定制 v-model:propName 语法
emits 声明 可不声明 建议声明,未声明的原生事件透传

全局属性的迁移最容易静默失败:Vue2 里挂的请求封装,升级后在页面里 this.$http 变成 undefined。Vue3 的正确姿势:

// main.js(Vue3) export function createApp() { const app = createSSRApp(App); app.config.globalProperties.$http = http; // 模板里仍可用 $http return { app }; }

组件模型的迁移细节

生命周期之外,组件间的三件套——通信、复用、插槽——在两条语法线里也有各自的姿势。mixin 转组合函数是复用方式的最大变化:Vue2 里把"倒计时"逻辑做成 mixin 混进页面,来源隐匿、命名冲突难查;Vue3 的做法是把同一逻辑写成组合函数,谁用了谁导入,来源清晰:

// Vue2 方式:countdown.mixin.js,混入后 this 里凭空多出 remaining 与 startCountdown export default { data() { return { remaining: 0 }; }, methods: { startCountdown(sec) { /* ... */ } } }; // Vue3 方式:组合函数,显式导入显式解构 import { ref } from 'vue'; export function useCountdown() { const remaining = ref(0); function startCountdown(sec) { remaining.value = sec; const timer = setInterval(() => { remaining.value > 0 ? remaining.value -= 1 : clearInterval(timer); }, 1000); } return { remaining, startCountdown }; }

自定义事件的声明:Vue3 建议在组件里用 emits 选项列出全部事件名,未声明的事件会落到根元素上变成原生透传,这在表单类组件上会造成"点一下触发两次"的怪象。插槽语法:具名插槽的 slot="name" 旧语法在 Vue3 线统一为 v-slot,混写期旧语法在部分端编译告警,值得在公约里直接禁用旧写法。provide 与 inject 的键在 Vue3 建议用 Symbol 导出,避免字符串键撞名——多端工程里组件库与业务层同时 provide 同名字段的概率不低,这属于低成本高收益的公约项。

老项目渐进升级的顺序

商城仓库的升级按四步走,每步都可独立回归。第一步,工程底座:新建 Vue3 工程跑通编译,把 manifest、pages.json 迁过来;第二步,公共层:请求封装、存储封装、工具函数先迁,它们多为纯逻辑,风险最低;第三步,叶子组件:从没有子组件的展示组件开始改写,逐步向上;第四步,页面:按访问频次从低到高迁移,核心交易页面最后动并重点回归。混写阶段两套写法可以共存于同一工程——编译器按组件处理,不要求全仓统一。唯一要避免的是同一个组件里 options 与 setup 混用,维护者会分不清状态到底定义在哪。

⚠️ 升级期间最容易踩的坑是"复制 Vue3 示例进 Vue2 工程":setup 语法在 Vue2 编译链里直接报错还好,若用兼容写法半通不通,问题会延迟到运行时才炸。贴示例前先验工程身份,这一步永远值得。

要点回顾

  • 验明工程身份看三处:vueVersion 字段、编译插件依赖、组件写法;
  • 页面生命周期是差异最密集地带,onLoad 仅页面可用、刷新逻辑放 onShow 是两条铁律;
  • 全局属性、过滤器、事件总线的迁移各有对应替代,混写期避免单组件内两套写法并存;
  • 渐进升级按"底座、公共层、叶子组件、页面"四步走,每步独立回归。

下一节把镜头推进到模板内部:你写的指令与事件,在各端分别编译成了什么。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U