什么是微前端
微前端其实有点类似于后端的微服务,把一个应用拆分为多个子应用,然后给多个团队去维护,最后统一在一个页面内进行整合
它的优点主要有三个
- 子应用的技术栈想用什么就用什么,不需要强制统一
- 每一个子应用单独开发,单独上线
- 一个模块崩了,不会导致整体崩了
它的缺点也主要有三个
- 整体结构变复杂了,排查问题更难
- 多套代码一起跑,整体应用占用内存会更多,会更卡
- 全局样式、全局变量等会冲突,处理起来更繁琐
与我历史工作经验结合
我回来仔细想了想,从这个思路来看,我们的 C 端项目、小程序其实也可以看作一种微前端。
就拿美团小程序来说,它的内容非常丰富,涵盖外卖、打车、医药、闪购等多个业务板块,每个子模块都由独立团队负责维护,最终统一集成到美团小程序里。
它虽然不像 B 端系统那样,通过 JS 沙箱做运行时隔离,但用了另一种方式,小程序自身的机制来实现模块隔离。
因为微信小程序本身不允许动态执行JavaScript,一旦检测到就会直接封禁,所以很多隔离工作都需要我们手动处理:
- 全局 storage 的隔离
- 全局公共方法的处理
- 打包依赖与路径的处理
- 通信方法
- 路由方法处理
我觉得这些思路,本质上和 B 端微前端的设计思想是完全相通的。
微前端的两种实现方式
微前端有两种主要的实现方式:一种是利用 iframe 实现,另外一种是通过 SPA 实现
iframe
- 优点
- 缺点
- 弹窗、路由、高度自适应很难处理,体验割裂
- 通信麻烦,性能差,多 iframe 容易卡顿
- 无法共享依赖,资源重复加载,体积大
spa
- 优点
- 体验流畅,像同一个应用,无页面割裂感
- 可共享依赖,减少重复打包,性能更好
- 路由、状态、全局拦截统一管理,更易维护
- 缺点
- 隔离靠沙箱,不如 iframe 彻底,仍可能冲突
- 改造成本高,需适配构建、生命周期
- 调试更复杂,问题定位比 iframe 麻烦
自己实现一个微前端
微权大致可以分为三部分:
- 主应用
- 次应用
- 微权能框架
其中要接入前端主应用和子应用,都需要进行改造
一般情况下,需要在主应用中调用微前端框架,对微前端进行注册
此应用里面需要区分一下环境,因为很多时候此应用不仅需要嵌入在微前端框架里面运行,它自己也可以单独运行
export const patchRouter = (globalEvent, eventName) => {
return function () {
const e = new Event(eventName);
globalEvent.apply(this, arguments); // 执行原始路由方法
window.dispatchEvent(e); // 派发自定义事件
}
};
export const rewriteRouter = () => {
// 劫持 pushState/replaceState
window.history.pushState = patchRouter(window.history.pushState, 'micro_push');
window.history.replaceState = patchRouter(window.history.replaceState, 'micro_replace');
// 监听自定义事件,触发路由处理
window.addEventListener('micro_push', turnApp);
window.addEventListener('micro_replace', turnApp);
// 监听浏览器前进后退
window.onpopstate = function () {
turnApp();
}
};
- 劫持路由方法:重写 pushState / replaceState,监听所有前端路由跳转。
- 触发统一处理:路由变化时自动派发事件,执行子应用切换逻辑。
- 兼容全场景:支持代码跳转、浏览器前进后退,保证路由行为一致。
获取第一个子应用,其实就是从一堆配置中找到当前被激活的配置。
我们可以注册主应用的生命周期。这个生命周期可以分为三段:
- before load
- mounted
- destroyed
这三个生命周期可以注册为一个函数。当我们需要触发生命周期,即触发注册的生命周期时,就可以直接调用相关的函数
微旋端框架的生命周期其实就是在调用主应用的生命周期,同时也在调用子应用的生命周期
在微前端框架中获取 HTML,可以通过 GET 请求,获取 HTML 之后可以通过 document.innerHTML 来插入之前获取的内容
危险温度降到里面,我们需要获取 GS 的相关内容,JS 又分为 ink 里面的 JS,script 里面的 JS其中 script 里的 gs 又分为:通过 src 引入的 gs,以及没有 src 直接在 script 内部的 js
我们需要提炼这些 JS
提炼 gs 之后,我们需要执行 gs
export const performScriptForFunction = (script) => {
new Function(script).call(window, window)
}
export const performScriptForEval = (script) => {
eval(script)
}
- 两个函数都是动态执行 JS 字符串脚本,用于微前端加载子应用代码。
- new Function 方案更安全:全局作用域、隔离性好、变量污染风险低。
- eval 方案风险更高:继承当前作用域、易造成变量混乱,生产不推荐。
危险端里面有一个快照杀香的概念。所谓快照杀香,就是在第一个子应用里面的全局变量,并不会在第二个子应用里面生效。
要实现这个快照功能,我们可以用一个 Proxy 来代理这个 Window 对象
当 Window 对应的应用生命周期被激活时,我们可以给快照生成进行复制。当子应用的生命周期结束之后,我们可以重置回这个快照生成
css 样式隔离
| 方案 |
时机 |
原理 |
优点 |
缺点 |
| CSS Modules |
构建时 |
类名加哈希:.btn → .btn_abc123 |
彻底、无兼容问题 |
要改代码、不兼容老项目 |
| minicss/scoped |
运行时 |
选择器加前缀 / 属性 |
零改造接入、兼容所有框架 |
复杂选择器(body/:root)可能漏 |
| Shadow DOM |
运行时 |
原生封闭 DOM 树 |
最彻底隔离 |
弹窗 / 覆盖层易出问题、兼容性一般 |
什么是微前端
微前端其实有点类似于后端的微服务,把一个应用拆分为多个子应用,然后给多个团队去维护,最后统一在一个页面内进行整合
它的优点主要有三个
它的缺点也主要有三个
与我历史工作经验结合
我回来仔细想了想,从这个思路来看,我们的 C 端项目、小程序其实也可以看作一种微前端。
就拿美团小程序来说,它的内容非常丰富,涵盖外卖、打车、医药、闪购等多个业务板块,每个子模块都由独立团队负责维护,最终统一集成到美团小程序里。
它虽然不像 B 端系统那样,通过 JS 沙箱做运行时隔离,但用了另一种方式,小程序自身的机制来实现模块隔离。
因为微信小程序本身不允许动态执行JavaScript,一旦检测到就会直接封禁,所以很多隔离工作都需要我们手动处理:
我觉得这些思路,本质上和 B 端微前端的设计思想是完全相通的。
微前端的两种实现方式
微前端有两种主要的实现方式:一种是利用 iframe 实现,另外一种是通过 SPA 实现
iframe
spa
自己实现一个微前端
微权大致可以分为三部分:
其中要接入前端主应用和子应用,都需要进行改造
一般情况下,需要在主应用中调用微前端框架,对微前端进行注册
此应用里面需要区分一下环境,因为很多时候此应用不仅需要嵌入在微前端框架里面运行,它自己也可以单独运行
获取第一个子应用,其实就是从一堆配置中找到当前被激活的配置。
我们可以注册主应用的生命周期。这个生命周期可以分为三段:
这三个生命周期可以注册为一个函数。当我们需要触发生命周期,即触发注册的生命周期时,就可以直接调用相关的函数
微旋端框架的生命周期其实就是在调用主应用的生命周期,同时也在调用子应用的生命周期
在微前端框架中获取 HTML,可以通过 GET 请求,获取 HTML 之后可以通过 document.innerHTML 来插入之前获取的内容
危险温度降到里面,我们需要获取 GS 的相关内容,JS 又分为 ink 里面的 JS,script 里面的 JS其中 script 里的 gs 又分为:通过
src引入的 gs,以及没有src直接在 script 内部的 js我们需要提炼这些 JS
提炼 gs 之后,我们需要执行 gs
危险端里面有一个快照杀香的概念。所谓快照杀香,就是在第一个子应用里面的全局变量,并不会在第二个子应用里面生效。
要实现这个快照功能,我们可以用一个 Proxy 来代理这个 Window 对象
当 Window 对应的应用生命周期被激活时,我们可以给快照生成进行复制。当子应用的生命周期结束之后,我们可以重置回这个快照生成
css 样式隔离