能今天做好的事就不要等到明天。以梦为马,学习趁年华。
一、微前端功能相关
一份好的roadmap很重要
- 如何进行路由劫持
- 如何渲染子应用
- 如何实现 JS 沙箱及样式隔离
- 提升体验性的功能
\
实现方案
- qiankun,icestark自己实现 JS 及样式隔离
- emp,Webpack 5 Module Federation(联邦模块)方案
- iframe 、WebComponent 等方案,浏览器原生隔离,但存在一些问题
and
| 方案 | 描述 | 优点 | 缺点 |
|---|---|---|---|
| Nginx路由转发 | 通过Nginx配置反向代理来实现不同路径映射到不同应用 | 简单,快速,易配置 | 在切换应用时会触发浏览器刷新,影响体验 |
| iframe嵌套 | 父应用单独是一个页面,每个子应用嵌套一个iframe | 实现简单,子应用之间自带沙箱,天然隔离,互不影响 | frame的样式显示、兼容性等都具有局限性;太过简单而显得low |
| npm包形式 | 子工程以NPM包的形式发布源码;打包构建发布还是由基座工程管理,打包时集成。 | 打包部署慢,不能单独部署 | 打包部署慢,不能单独部署 |
| 通用中心路由基座式 | 子工程可以使用不同技术栈;子工程之间完全独立,无任何依赖;统一由基座工程进行管理,按照DOM节点的注册、挂载、卸载来完成。 | 不限定技术栈,单独部署 | 通信方式不够灵活 |
| 特定中心路由基座式 | 子业务线之间使用相同技术栈;基座工程和子工程可以单独开发单独部署;子工程有能力复用基座工程的公共基建。 | 通信方式多,单独部署 | 限定技术栈 |
Iframe:iframe 最大的特性就是提供了浏览器原生的硬隔离方案,不论是样式隔离、js 隔离这类问题统统都能被完美解决。但他的最大问题也在于他的隔离性无法被突破,导致应用间上下文无法被共享,随之带来的开发体验、产品体验的问题。
如上摘自Why Not Iframe。
场景分类
1.单实例:当前页面只存在一个子应用,一般使用 qiankun 就行。
2.多实例:当前页面存在多个子应用,可以使用浏览器原生隔离方案,比如 iframe 或者 WebComponent 这些。
qiankun做了什么
二、Single-SPA vs qiankun
Single-SPA
基座
在基座里我们调用single-spa提供给我们的registerApplication和start的方法(4个参数:appNameOrConfig、appOrLoadApp、activeWhen、customProps)。
子项目
最重要的就是提供三个方法 bootstrap、mount、unmount。+自身的打包方式“umd”。
- 微前端模式下,single-spa会在window上挂在一个singleSpaNavigate的属性。
- 这时候我们需要将public_path改成子项目中的地址。
if(window.singleSpaNavigate){
****webpack_public_path = 'http: //localhost:10000/'
}
缺点
不够灵活不能动态加载js文件 如10000个将...、样式不隔离、没有js沙箱的机制。
QianKun
qiankun基于Single-SPA, 提供了更加开箱即用的 API ( single-spa + sandbox+ import-html-entry ) 做到了,技术栈无关、并且接入简单(像iframe 一样简单)。
Tips:因为是通过fetch方法直接把html插入到容器里,所以子项目需要允许跨域才行。
主应用
if (window.POWERED_BY_QIANKUN) { // 动态添加publicPath
webpack_public_path = window.INJECTED_PUBLIC_PATH_BY_QIANKUN;
}
子应用
- 打包配置:UMD、跨域
- 子应用通常也分为三个生命周期:bootstrap(常用于配置子应用全局信息)、mount(常用于渲染子应用)、unmount
三. Demo手记
微应用分为有 webpack构建和无 webpack构建项目,有 webpack的微应用需要做的事情有:
\
- 新增
public-path.js文件,用于修改运行时的publicPath。(运行时的 publicPath 和构建时的 publicPath 是不同的) - 微应用建议使用
history模式的路由,需要设置路由base,值和它的activeRule是一样的。 - 在入口文件最顶部引入
public-path.js,修改并导出三个生命周期函数。 - 修改
webpack打包,允许开发环境跨域和umd打包。
\
- 三种前端框架的路由,都支持
hash和history模式,不同的模式在qiankun中略有差别。 activeRule使用location.pathname区分微应用。
现象:
- 关于样式隔离,只对class起作用,其他例如标签会造成子项目覆盖父项目。
- 路由,子项目A的路由2,被切换为子项目B 再回来,项目A路由会回到home页。
四. 遇到的小问题
一个人走的快,一群人走得远
- 文档中讲到:一个页面同时展示多个微应用时,需要使用 loadMicroApp 来加载。
也讲到过,如果微应用不是直接跟路由关联的时候,你也可以选择loadMicroApp 手动加载微应用的方式。
- 可能使用不得法,出现了一些奇怪的现象,react很强劲-无论注册顺序如何调整,个人也没有很理解单个页面多个微应用时其中子项目路由分别的匹配逻辑。
五. 相关实现原理浅谈
日常浏览,量变引起质变
应用通信
· 基于URL来进行数据传递,但是传递消息能力弱:父子通信ok、子项目间?
· 基于 CustomEvent 实现通信:可以看做是发布订阅模式的一种实现。
· 基于props主子应用间通信:demo未成功
· 使用全局变量、 Redux 进行通信:
css隔离方案
- 子应用之间样式隔离:
· Dynamic Stylesheet 动态样式表,当应用切换时移除老应用样式,添加新应用样式
- 主应用和子应用之间的样式隔离:
· BEM (Block Element Modifier) 约定项目前缀
· CSS-Modules 打包时生成不冲突的选择器名
如:postcss-plugin-namespace,给各自的项目class全局加上一个各自的命名空间。qiankun已通过其他方式实现。
· Shadow Dom 真正意义上的隔离
js沙箱机制
· ****快照沙箱,在应用沙箱挂载或卸载时记录快照,在切换时依据快照恢复环境 (无法支持多实例);modifyPropsMap、windowSnapshot。唯一缺点就是性能慢点。
· Proxy 代理沙箱,不影响全局环境,不需要直接更改全局window属性!
一般快照和 Proxy 沙箱都是需要的,无非前者是后者的降级方案,毕竟不是所有浏览器都支持 Proxy 的。
六. 改善/优化
哲学就是解决“如何处理问题”的问题
学习方法、学科的基本思路、研究历程(历史)、背景(学习只是一种为了解决某些问题而需要去做的事情,找找那个问题吧💡)
关键词:prefetch、资源缓存机制、全局通信及状态
import-html-entry
关键依赖介绍:基座中的import-html-entry
他解决了:
- 加载及解析文件 - 如访问/vue下的link、script、img文件,子项目用的自己的相对路径,这里就需要转换。
- prefetch:在未处理情况下,是匹配一个子应用成功后才去加载子应用,这种方式其实不够高效。我们更希望用户在浏览当前子应用的时候就能把别的子应用资源也加载完毕,这样用户切换应用的时候就无需等待了。
如上代码中window.requestIdleCallback() 方法,他将在浏览器的空闲时段内调用的函数排队。这使开发者 能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应。
- 资源缓存机制: 简单来说就是创建一个对象来缓存每次请求下来的文件内容,下次请求的时候先判断对象中是否存在,存在则直接拿出来用。
\
参考文章:
文中链接🔗及