前提 blog.csdn.net/yezi__6/art…
微前端方案有:京东的micro-app,腾讯的wujie,阿里的qian kun等 qiankun相较于iframe具有一些优势:
- 更好的路由前进后退
- 更自然的弹窗遮罩层覆盖可视范围
- 更方便的页面间消息传递
微前端的定义
微前端是一种前端架构方法,将一个庞大的前端应用拆分为多个独立灵活的小型应用,每个应用都可以独立开发、独立运行、独立部署,再将这些小型应用联合为一个完整的应用。
微前端的实现
当路由切换时,下载对应应用的代码,在容器里运行。
核心原理
是利用了 JavaScript
的 Proxy
对象实现沙箱环境。这种沙箱环境可以实现对子应用的隔离,使其不影响主应用或其他子应用。
微前端框架两大共性问题
- 应用的加载与切换。包括路由的处理、应用加载的处理和应用入口的选择。
- 应用的隔离与通信。JS的隔离、样式的隔离、父子之间的通信问题。
qiankun
qiankun注册微应用的方式
手动模式:使用loadMicroApp手动注册微应用
自动模式:使用registerMicroApps+start,路由变化加载应用
应用之间的通信
localStorage/sessionStorage
通过路由参数共享
官方提供的 props
官方提供的 actions
使用vuex或redux管理状态,通过shared分享
qiankun JS沙箱
-
[快照沙箱]
SnapshotSandbox
:记录window
对象,每次 unmount 都要和微应用的环境进行 Diff原理
: 沙箱激活时,会讲window的快照信息存在windowSnapshot中,如果modifyPropsMap有值,还需要还原上次的状态;激活期间,可能修改了w indow的数据,退出沙箱时,将修改过的信息存在modifyPropsMap里,并且把window还原成初始进入的状态原理(代码版)
:通过new snapshotSanbox(),激活沙箱,将window的快照信息存到windowSnapshot里面,并且从modifyPropsMap还原上次修改的值,退出沙箱,对windowSnapshot和当前window做diff,将变更的值存到modifypropsMap,同时将windowSnapshot还原到window。
-
[单例沙箱]
LegacySandbox
:在微应用修改window.xxx
时直接记录 Diff,将其用于环境恢复 -
[代理沙箱]
ProxySandbox
:为每个微应用分配一个fakeWindow
,当微应用操作window
时,其实是在fakeWindow
上操作。- 激活沙箱后,每次对window取值时,先从自己沙箱环境的fakeWindow里面找,如果不存在,就从rawWindow(外部的window)里找;当对沙箱内部的window对象赋值时,会直接操作fakeWindow,而不会影响到rawWindow.
要和这些沙箱结合起来使用,qiankun 会把要执行的 JS 包裹在立即执行函数中,通过绑定上下文和传参的方式来改变 this
和 window
的值,让它们指向 window.proxy
沙箱对象,最后再用 eval
来执行这个函数。
简化版qiankun JS沙箱实现原理代码
// 1. 使用 Proxy 创建一个空对象,该对象是子应用运行的沙箱环境
function createSandbox() {
// 原始的 window 对象
const rawWindow = window;
// 2. 创建一个代理,在这个代理上捕获对 window 的读/写操作
const sandbox = new Proxy({}, {
// 当读取属性值时触发
get(target, key) {
// 3. 如果子应用读取 window 的属性,实际上是读取原始 window 上的属性
return rawWindow[key];
},
// 当设置属性值时触发
set(target, key, value) {
// 4. 当子应用试图修改 window 属性时,实际上修改的是这个代理对象,而不是原始的 window
// 这样就实现了对子应用对 window 修改的隔离
target[key] = value;
return true;
}
});
return sandbox;
}
// 使用
const sandbox = createSandbox();
// 子应用代码运行在沙箱中,比如:
sandbox.someProperty = 'Hello from child app';
console.log(window.someProperty); // undefined
console.log(sandbox.someProperty); // 'Hello from child app'
// 注意:这只是 qiankun 沙箱实现的一个简化版本,真实的 qiankun 沙箱实现会考虑更多的细节和兼容性问题。
qiankun CSS沙箱
背景
主应用中引入多个子应用时,子应用的样式会相互影响。(因为子应用的样式是全局的)。
- 子应用样式不会影响到其他子应用或主应用,同时支持全局样式
- 主应用样式可以覆盖各子应用下的指定的样式,不容易影响到其他子应用
qiankun提供的样式隔离方案
- Qiankun 的样式隔离主要分为 strictStyleIsolation 以及 experimentalStyleIsolation 两种
strictStyleIsolation(主要是通过Shadow DOM
实现)
- Shadow DOM 子应用的根节点创建一个shadow root,最终整个应用的所有DOM形成一颗shadow tree。(特点:内部所有节点的样式对树外面的节点无效,实现样式隔离)
- 取出当前元素内容
- 生成shadowDOM
- 取出内容放到shadowDOM
- 清除这个元素,追加到shadow DOM上
特例:当启用strictStylelsolation时,Element-plus组件的部分样式会失效?
原因:Element-plus的全局变量是通过:root选择器作用到根节点的,但是在shadow dom中,无法通过:root选中根节点,导致部分样式失效。【juejin.cn/post/726231…
解决思路:自定义loader,在webpack打包文件时把:root替换成:host
const myStyleLoader = function(source){
return source.replace(/:root/g,':host')
}
module.exports = myStyleLoader
修改webpack配置规则
modules:{
rules:[
{
test:/\.scss$/,
include:[path.resolve(_dirname,'node_modules/element-plus/theme-chalk')]
use:['style-loader','css-loader','my-style-loader','sass-loader']
}
]
}
experimentalStyleIsolation(主要是通过scoped css
实现)[在子应用下面的样式会包一个特殊的选择器规则限定影响范围]
- Scoped CSS 则是对
style
元素的 CSS 文本进行处理,在原有选择器上添加下个父类选择器,以此做样式隔离- 原理:将微应用的的文本提取出来,将所有的选择器进行替换,样式就只会作用在div[data-app-name=子应用名称]元素上
坑:动态类的样式会丢失?