三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Esmx 微前端多版本共存原理:Vue2 与 Vue3 如何在同一个应用里和平共处?

Esmx 微前端多版本共存原理:Vue2 与 Vue3 如何在同一个应用里和平共处?

Esmx 微前端多版本共存原理:Vue2 与 Vue3 如何在同一个应用里和平共处?

【免费下载链接】genesisNext-generation micro-frontend framework based on ESM, sandbox-free with zero runtime overhead, supporting multi-framework hybrid development项目地址: https://gitcode.com/gh_mirrors/genesis8/genesis

当你的老项目还停留在 Vue 2,而新业务已经全面拥抱 Vue 3,你大概率经历过"技术债地狱":要么忍受繁琐的 iframe 嵌入,要么投入高昂成本做一次性重写。微前端框架 Esmx 给出的答案是——让不同大版本的框架在同一个页面里"各过各的",互不打扰,甚至连沙箱都不需要。这套基于原生 ESM(ES Modules)的多版本共存机制,正是 Esmx 作为下一代微前端框架最核心的能力之一。本文就用官方示例中 Vue2 与 Vue3 共存的真实案例,拆解它是如何做到的。

为什么 Vue2 和 Vue3 共存这么难?

Vue 2 和 Vue 3 虽然同宗同源,但它们的运行时是两套完全独立的代码:响应式系统重写、虚拟 DOM 重写、vue-server-renderer@vue/server-renderer各成一派。如果两个版本同时加载,很容易出现:

  • 全局变量冲突:两个运行时都往window上挂东西,互相覆盖;
  • 单例被劫持:某个组件拿到的Vue不是它期望的那个版本,行为异常;
  • 样式与状态串扰:子应用之间 DOM、事件互相污染。

传统微前端方案大多依赖JS 沙箱(比如用 Proxy 拦截全局读写)来隔离环境,但沙箱本身有性能损耗,还会带来各种"隔离不彻底"的诡异 bug。Esmx 的思路截然不同:从源头消灭冲突,而不是事后兜底

核心机制一:原生 ESM 天然自带隔离能力

Esmx 之所以不需要沙箱,是因为它把隔离这件事交给了浏览器和 Node.js 的原生能力——ES Module。ESM 模块天然具有:

  • 模块作用域:每个模块的顶层变量互不可见;
  • 静态依赖图:依赖关系在构建期就完全确定;
  • 独立实例:同一个 URL 的模块是单例,但不同 URL 就是不同实例。

在此基础上,Esmx 在构建期就为每个微应用生成独立的Import Map,并通过scopes(作用域)把不同框架的模块实例隔离开。你可以直接查看它的核心实现:import-map.ts 中的createScopesMap,它会为每个模块的代码及其代码分割 chunk 单独建立一张"地址对照表",互不干扰。

核心机制二:provides / uses 声明式依赖协议

在 Esmx 中,每个微应用要在自己的package.jsonesmx字段里声明两件事:

  • provides(我提供什么):本模块对外共享的第三方包;
  • uses(我用什么):本模块依赖哪些已挂载的模块。

看官方示例里的 Vue2 子应用声明:ssr-micro-vue2/package.json,它通过provides: ["vue"]明确宣告:这个模块提供 Vue(2.7.16 版本)。而 Vue3 子应用则在 ssr-micro-vue3/package.json 中使用 Vue 3.5.34。

关键点来了:Esmx 的所有权规则是"每个 (包名, 大版本号) 只有一个拥有者"。Vue 2 的大版本号是 2,Vue 3 的大版本号是 3,它们天然就是两个不同的"所有权键",所以可以作为两个完全独立的孤岛共存。如果两个模块同时声明提供 Vue 3,那才会触发E_DUP_PROVIDER错误。这套协议的完整设计见官方 RFC 文档:0001-module-protocol.md。

核心机制三:每个框架版本拥有独立的渲染管线

多版本共存的另一个难点在服务端渲染(SSR)。Vue2 的 SSR 用的是vue-server-renderer,Vue3 用的是@vue/server-renderer,两者 API 完全不同。Esmx 的处理方式是:每个子应用自带独立的服务端渲染入口

  • Vue2 子应用的渲染器位于 server-renderer.ts,并在package.json中通过exports单独声明为./vue-server-renderer,客户端侧禁用(client: false);
  • Vue3 子应用的渲染逻辑则由 ssr-micro-vue3-shared 提供。

这样,在同一个 Node 进程里,Vue2 用 Vue2 的渲染器、Vue3 用 Vue3 的渲染器,服务端也能各走各的管道,互不抢占。

官方示例实战:16 个微应用同台演出

说了这么多理论,不如看看真实效果。Esmx 仓库里内置了一个16 个微应用组成的大型示例 Hub,除了 Vue2、Vue3,还有 React、Preact、Solid、Svelte、Lit 以及纯 HTML 应用,全部通过一个入口聚合。路由合并逻辑写在 ssr-micro-hub/src/routes.ts:

import { routes as vue2Routes } from 'ssr-micro-vue2/routes'; import { routes as vue3Routes } from 'ssr-micro-vue3/routes'; // ...React、Solid、Svelte 等 16 个子应用路由 export const routes: RouteConfig[] = [ ...baseRoutes, ...vue2Routes, ...vue3Routes, // ...其余子应用路由 ];

启动整个示例(仓库根目录执行pnpm install后运行)就能看到:左侧导航栏里 Vue 2 和 Vue 3 两个子应用并排陈列,点击切换时,右边渲染的内容分别由 Vue 2.7 和 Vue 3 驱动,页面标题、代码展示、交互状态全部正常,互不影响。

上图中左侧导航栏高亮的 "Vue 2" 与 "Vue 3" 正是同一 Hub 里两个版本共存的直观证据:同一个路由系统、同一套导航壳,内部却是两套完全独立的 Vue 运行时。整个演示主页面的完整效果可参考 hub-demo-index 截图,这些截图同时也是仓库里 Playwright 视觉回归测试的基准图,说明多版本共存是可持续验证的稳定性能力,而不是"碰巧能跑"的 demo。

和其他微前端方案比,Esmx 强在哪?

对比维度传统 JS 沙箱方案Esmx(ESM 方案)
运行时隔离依赖 Proxy 拦截,有性能损耗原生 ESM 模块作用域,零额外开销
多版本框架共存需要大量配置和补丁按大版本号天然隔离,声明即生效
服务端渲染沙箱难以覆盖 Node 端每个子应用独立渲染管线,SSR 友好
资源加载需自定义加载器原生<script type="module">加载
上手成本概念多、坑多只在package.json里写几行声明

快速上手:三步搭建你自己的 Vue2 + Vue3 共存应用

想亲手体验多版本共存,跟着下面三步走:

  1. 克隆仓库并安装依赖运行git clone https://gitcode.com/gh_mirrors/genesis8/genesis克隆项目,然后在根目录执行pnpm install(仓库使用 pnpm workspace 管理)。

  2. 启动微前端 Hub 示例进入 examples/micro-app 目录,运行pnpm start,浏览器访问http://localhost:3000,即可看到 Vue2、Vue3 及其他框架子应用同台演出。

  3. 阅读源码理解原理重点看三个文件:ssr-micro-vue2/package.json(Vue2 的 provides 声明)、ssr-micro-vue3/package.json(Vue3 的 uses 声明),以及 import-map.ts(Import Map 作用域生成逻辑)。

写在最后

Esmx 用原生 ESM 取代沙箱,用声明式协议取代运行时仲裁,把"多版本共存"从一件需要大量黑魔法的事情,变成了构建期就能确定、零运行时开销的确定性行为。如果你的团队正被"老框架带不动、新框架迁不动"折磨,不妨试试这个思路——让 Vue2 和 Vue3 不再是你死我活的对手,而是同一屋檐下的室友。

【免费下载链接】genesisNext-generation micro-frontend framework based on ESM, sandbox-free with zero runtime overhead, supporting multi-framework hybrid development项目地址: https://gitcode.com/gh_mirrors/genesis8/genesis

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

← 返回列表