介绍微前端
可以帮助我们将多个独立的前端应用整合成一个整体,并且可以独立开发、独立部署、独立运行。
- 主应用:整个微前端应用的入口,负责加载和管理子应用。
- 子应用:独立的前端应用,可以独立开发、独立部署、独立运行。
- 生命周期:主应用和子应用之间的生命周期钩子,用于控制应用的加载、启动、卸载等过程。
- 沙箱:用于隔离子应用的 JavaScript 执行环境,防止子应用之间的冲突和污染。
- 应用间通信:主应用和子应用之间的通信机制,用于实现数据共享和事件传递。
优劣势
优势:
- 可以将多个独立的前端应用整合成一个整体,提高了代码复用性和可维护性。
- 技术兼容性好,各个子应用可以基于不同的技术架构。
- 耦合性更低,可以独立开发、独立部署、独立运行,提高了开发效率和部署灵活性。
- 代码库更小、内聚性更强。
- 可以实现子应用之间的通信和数据共享,提高了应用之间的协作能力。
- 可以实现子应用的按需加载和动态卸载,提高了应用的性能和用户体验。
缺点:
- 子应用间的资源共享能力较差,使得项目总体积变大。
- 需要对现有代码进行改造(指的是未按照微前端形式编写的旧工程)。
实现原理
一般来说,微前端需要解决的问题分为两大类:
- 应用的加载与切换
- 应用的隔离与通信
应用的加载与切换需要解决的问题包括:路由问题、应用入口、应用加载;应用的隔离与通信需要解决的问题包括:js隔离、css样式隔离、应用间通信。
single-spa很好地解决了路由和应用入口两个问题,但并没有解决应用加载问题,而是将该问题暴露出来由使用者实现(一般可以用system.js或原生script标签来实现);qiankun借助import-html-entry实现了应用加载,并给出了js隔离、css样式隔离和应用间通信三个问题的解决方案。
路由问题
single-spa是通过监听hashChange和popState这两个原生事件来检测路由变化的,它会根据路由的变化来加载对应的应用。
应用入口
采用的是协议入口,即只要实现了single-spa的入口协议规范,它就是可加载的应用。暴露bootstrap、mount、unmount三个函数。
应用加载
qiankun使用了一个更完整的应用加载方案:import-html-entry。该方案的主要思路是允许以html文件为应用入口,然后通过一个html解析器从文件中提取js和css依赖,并通过fetch下载依赖。将外联样式处理成内部样式表,并且构造了一个可以在隔离环境下执行应用脚本的方法execScripts。
js隔离
import-html-entry构造了一个可以在隔离环境下执行应用脚本的方法execScripts,qiankun要做的就只需要在加载一个应用时为其初始化一个proxy传递进来即可。
export default class ProxySandbox implements SandBox {
...
constructor(name: string) {
...
const proxy = new Proxy(fakeWindow, {
set () { ... },
get () { ... }
}
}
}css隔离
目前qiankun主要提供了两种样式隔离方案,一种是基于shadowDom的;另一种则是实验性的,思路类似于Vue中的scoped属性,给每个子应用的根节点添加一个特殊属性,用作对所有css选择器的约束。
- strictStyleIsolation,基于shadowDom。但是某些UI框架可能会生成一些弹出框直接挂载到document.body下,此时由于脱离了shadow tree,所以它的样式仍然会对全局造成污染。
- experimentalStyleIsolation,类似于scoped属性。但是不支持@ keyframes,@ font-face,@ import,@ page
应用通信
一般来说,微前端中各个应用之前的通信应该是尽量少的,而这依赖于应用的合理拆分。反过来说,如果你发现两个应用间存在极其频繁的通信,那么一般是拆分不合理造成的,这时往往需要将它们合并成一个应用。
当然了,应用间存在少量的通信是难免的。qiankun官方提供了一个简要的方案,思路是基于一个全局的globalState对象。这个对象由基座应用负责创建,内部包含一组用于通信的变量,以及两个分别用于修改变量值和监听变量变化的方法:setGlobalState和onGlobalStateChange。
子应用如何实现按需加载
- 子应用可以通过导出一个异步加载函数来实现按需加载:bootstrap、mount、unmount三个钩子函数。
- 主应用中可以通过loadMicroApp动态加载子应用,也可以匹配路由的方式加载子应用。
子应用与主应用的通信
- 子应用:
window.parent.postMessage - 主应用:可以通过
onGlobalStateChange方法来监听子应用的状态变化
子应用与其他子应用的通信
- 子应用:可以通过 window.dispatchEvent 方法来触发自定义事件
- 其他子应用:可以通过 window.addEventListener 方法来监听自定义事件
子应用如何实现路由跳转
子应用可以通过 history.pushState 方法来实现路由跳转。
子应用如何实现样式隔离
可以通过 CSS Modules 或 CSS-in-JS 等技术来实现样式隔离。
子应用共享工具库的思考
- 为什么不通过主应用分发:各子应用,依赖的组件库/工具库版本不一致,如果全部升级的话,影响面不可控。
- 可以通过webpack分包的方式,优化加载
使用中的问题
弹窗组件样式失效
弹窗的dom挂载到了body上,应该挂载到子应用上。
子应用资源跨域
子应用静态资源一定要支持跨域。由于 qiankun 是通过 fetch 去获取子应用的引入的静态资源的,所以必须要求这些静态资源支持跨域。
子应用加载的资源会 404
需要使用 webpack 运行时 publicPath 配置:
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;子应用切换时视觉问题
不要单独打出来 css chunk,直接把 css 插到 dom 中。
styleLoader: {
injectType: 'singletonStyleTag'
}