Vue 2(含 <2.6)如何升级兼容 Composition API 原理
本文详细说明本项目如何在 Vue 2(包括低于 2.6 的版本)上通过插件注入与运行时适配,提供与 Vue 3 Composition API 接近的开发体验。内容覆盖安装入口、启用机制、运行时上下文桥接、响应式与计算/侦听适配、插槽与生命周期、兼容策略、执行时序、常见问题与限制,以及关键代码索引。 阅读本文时,请配合 composition-api 源码食用 https://github.com/vuejs/composition-api
一、背景与总体思路
- 目标:在不依赖 Vue 3 内核(Proxy/Effect)的前提下,让 Vue 2 应用可以使用
setup()、ref/reactive/computed/watch等 Composition API。 - 总体方法:通过
Vue.use(Plugin)安装插件,在全局混入与选项合并层面“接入” Vue 2 的组件初始化流程,同时复用 Vue 2 的Observer/Dep/Watcher作为底层响应式机制。 - 关键桥接:构造 Vue3-like 的“当前实例上下文”(
getCurrentInstance()等),并将返回的setup对象或函数正确注入到 Vue 2 的渲染/实例属性中。
二、安装入口与引入即用
- 主入口导出并安装插件:
src/index.ts:15导出Plugin;src/index.ts:25在浏览器存在window.Vue时自动执行window.Vue.use(Plugin)。- CommonJS 入口:
index.js:3-6按NODE_ENV指向 dev/prod 构建文件。
- 安装对象与执行:
src/install.ts:80-82定义Plugin.install(Vue);内部调用install(Vue)完成注册。
- 为什么“引入文件即可生效”:
- 当以 CDN 或打包方式引入入口文件,若全局存在
Vue,会自动Vue.use(Plugin);否则在应用入口手动Vue.use(Plugin)即完成接入。
- 当以 CDN 或打包方式引入入口文件,若全局存在
三、启用机制:setup 合并策略 + 全局混入
- 选项合并策略:
src/install.ts:64-74为setup设置合并规则,支持父子组件的setup共存与返回对象的融合,约束执行顺序与一致性。
- 全局混入注入:
src/mixin.ts:30-42通过Vue.mixin在beforeCreate阶段调用functionApiInit,将 Composition API 接入每个组件实例。- 保持渲染上下文:
src/mixin.ts:53-59包装render,在渲染时激活当前组件实例上下文。 - 执行顺序控制:
src/mixin.ts:75-85重写data解析顺序,保证setup先于data执行,以避免低版本 Vue 的选项时序差异导致行为不一致。
setup返回值处理:- 返回函数:作为渲染函数使用(
src/mixin.ts:106-114),直接参与 VNode 生成。 - 返回对象:对
ref/reactive/function等进行自动解包与保护,并挂载到实例上(src/mixin.ts:115-147)。
- 返回函数:作为渲染函数使用(
- 局部响应式绑定:
- 为包含响应式子项(如数组)的对象执行局部
defineReactive,减小副作用面(src/mixin.ts:161-183)。
- 为包含响应式子项(如数组)的对象执行局部
四、运行时上下文桥接(Vue2 → Vue3 风格)
- 当前实例管理与暴露:
getCurrentInstance():src/runtimeContext.ts:232-234。- 激活/恢复实例:
src/runtimeContext.ts:104-110,261-268,确保生命周期与渲染期间上下文一致。
- Vue3-like 实例描述:
- 将 Vue2 vm 映射为
ComponentInternalInstance近似结构(src/runtimeContext.ts:241-320)。
- 将 Vue2 vm 映射为
- 安装状态与多实例防护:
- 已安装判定与重复安装警告:
src/runtimeContext.ts:45-48,72-83;src/install.ts:43-49。 - 版本校验提示:
src/install.ts:52-62。
- 已安装判定与重复安装警告:
五、响应式适配:reactive/readonly/ref
- 复用 Vue2 响应式:
observe/defineReactive作为响应式基石(src/reactivity/reactive.ts:231-249)。- 访问控制与自动解包:
defineAccessControl/proxy(src/reactivity/reactive.ts:52-114),在读取时自动解包ref,在写入只读对象时进行拦截。 - 原始对象关联与跳过标记:通过
__ob__、SKIPFLAG/rawSet标识(src/reactivity/reactive.ts:160-176,251-281)。
六、计算属性与侦听(computed/watch/watchEffect)
- computed 适配:
- 有 vm:直接用 Vue2
Watcher构建 lazy 计算,提供evaluate/depend(src/apis/computed.ts:26-57)。 - 无 vm/SSR:创建“宿主组件”承载
computed(src/apis/computed.ts:62-83)。
- 有 vm:直接用 Vue2
- watch / watchEffect:
- 基于
$watch(getter, callback, { immediate, deep, sync })(src/apis/watch.ts:379-387)。 - 补丁清理:劫持
teardown注入副作用清理(src/apis/watch.ts:192-200,402-407),避免内存泄漏与残留订阅。
- 基于
七、插槽与生命周期
- 插槽代理:
- 将 Vue2 插槽转换为函数式访问,使其更接近 Vue3 的
slots使用范式(src/utils/instance.ts:161-180、src/utils/helper.ts:41-55)。
- 将 Vue2 插槽转换为函数式访问,使其更接近 Vue3 的
- 生命周期钩子:
- 统一包装调用并维护当前实例上下文(
wrapHookCall),各onMounted/onUpdated/...以同样方式暴露(src/apis/lifecycle.ts:33-58)。
- 统一包装调用并维护当前实例上下文(
八、对 Vue<2.6 的兼容策略
- 不依赖 Vue3 内核:完全复用 Vue2 的
Observer/Dep/Watcher。 - 能力缺失的场景下提供回退:通过宿主组件或代理机制补足(例如无 vm 的
computed)。 - 插槽与
scopedSlots的差异用代理函数化统一访问,屏蔽低版本差异。 setup专属合并与时序控制,避免低版本选项解析导致的执行顺序问题。- 严格的安装/多实例防护,降低复杂构建/微前端场景下上下文错配的风险。
九、典型执行时序(组件创建)
- 应用引入插件,自动或手动
Vue.use(Plugin)(src/index.ts:25)。 - 全局混入生效,组件在
beforeCreate进入functionApiInit(src/mixin.ts:30-42)。 - 包装渲染与数据解析,保证
setup优先执行(src/mixin.ts:53-59,75-85)。 - 构造
SetupContext并执行setup:- 返回函数 → 设为
render(src/mixin.ts:106-114)。 - 返回对象 → 自动解包并挂载为实例属性(
src/mixin.ts:115-147)。
- 返回函数 → 设为
- 如返回值包含响应式子项,进行局部
defineReactive(src/mixin.ts:161-183)。 - 生命周期钩子按需调用,期间激活当前实例上下文(
src/apis/lifecycle.ts:33-58、src/runtimeContext.ts:104-110)。
十、常见问题与限制
- 必须先安装:未
Vue.use(Plugin)就使用 API 会触发断言/警告(src/runtimeContext.ts:50-57)。 - 重复安装与多 Vue 构造:会给出警告,避免上下文错配(
src/install.ts:43-49、src/runtimeContext.ts:72-83)。 - 行为差异:由于底层是 Vue2 的响应式系统,个别边界行为与 Vue3 可能不完全一致(例如深层 Proxy 行为、依赖收集的粒度)。
- SSR/无实例:
computed/watch会走宿主组件或降级路径,性能与时序需关注。
十一、关键代码索引(含行号)
- 入口与安装:
src/index.ts:15,25;index.js:3-6- 插件安装:
src/install.ts:64-78,80-82
- 运行时上下文:
- 安装状态与校验:
src/runtimeContext.ts:45-48,72-83 - 当前实例与映射:
src/runtimeContext.ts:104-110,232-234,241-320,261-268
- 安装状态与校验:
- 混入与 setup 注入:
src/mixin.ts:30-42,53-59,65-73,75-85,87-159,161-183
- 响应式适配:
src/reactivity/reactive.ts:52-114,160-176,231-249,251-281
- computed/watch:
src/apis/computed.ts:26-57,62-83src/apis/watch.ts:192-200,379-387,402-407
- 插槽代理:
src/utils/instance.ts:161-180;src/utils/helper.ts:41-55
- 生命周期:
src/apis/lifecycle.ts:33-58
十二、快速使用指南
- 安装:在入口文件中执行
import Plugin from '...' ; Vue.use(Plugin)。 - 组件:
- 使用
setup(props, ctx)返回对象或渲染函数;对象中使用ref/reactive/computed等 API。 - 在
onMounted/onUpdated/...中编写副作用逻辑,watch/watchEffect根据需要侦听与清理。
- 使用
十三、总结
- 该插件通过安装与全局混入,将 Composition API 嵌入到 Vue 2 的组件初始化与响应式系统之中。
- 响应式与计算/侦听复用 Vue2 的
Observer/Dep/Watcher,并在缺失能力的场景下提供宿主/代理回退。 - 运行时上下文桥接保证了
getCurrentInstance、插槽、生命周期的可用与一致体验,从而实现“引入即用”的 Composition API 支持。