微前端源码总结| 8月更文挑战

200 阅读3分钟

这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战

微前端的基础概念

混合多个独立模块(使用独立技术框架)到单个应用,解决 “巨石” 应用的问题。这里的单个应用被称为 “基座”。多个独立模块被称为 “子应用”。

微前端可能遇到的问题

  • 如何实现应用无需刷新页面即可切换不同模块(技术框架)的应用
  • 每个独立模块(技术框架)的按需加载
  • JS 沙箱、CSS 隔离
  • 单个页面同时加载不同模块(技术框架)的消息共享

如何实现应用无需刷新页面即可切换不同模块(技术框架)的应用

首先,假设有 A B 两个 SPA 应用,分别使用了 vue、react 技术栈,“基座” 应用(黑框内整体)使用了 angular。“基座” 应用有左侧的切换 tab,点击 A模块 即加载 A 应用在 id = "a" 处,点击 B模块 即加载 B 应用在 id = "b" 处。

  image.png

对于 A B 两个应用,需要配置以下几个参数:

  • 触发时机。(例如,当 location.href 为 /a 开头时,加载 A 模块)
  • 应用入口。(在 Single-SPA 框架中,仅支持 1.配置本地路径的子应用 2.配置systemJS加载的远程子应用。在 umijs/qiankun 中支持配置远程 SPA 子应用
  • 应用的生命周期函数。(bootstrap / mount / unmount / ...)

 

然后,当 “基座” 应用启动后,会读取子应用的配置参数(触发时机、应用入口、应用生命周期函数),然后逐一匹配触发时机,当满足触发时机,则读取应用入口并加载,执行对应的声明周期。当需要切换 子应用 时,重新执行该流程,并在切换 子应用 时,执行需要销毁的 子应用 的 unmount 生命周期。

每个独立模块(技术框架)的按需加载

由上一个问题可知,每个独立模块的加载与卸载完全取决于配置的第一个参数(触发时机)。因此,即实现了每个独立模块的按需加载。

 

微前端的 JS 沙箱机制、CSS 隔离方案

Single-SPA 框架中并未提供 JS 沙箱。但当使用微前端架构进行组织多个独立模块时,又必然会遇到 JS 互相污染的情况。所以 umijs/qiankun 提供了一套 JS 沙箱机制用于解决该问题。

  • 首先通过 Proxywindow 进行劫持,当触发 get、set 时保存获取和设置的值。如果获取的值是正常的函数,则添加 Symbol 标记。
  • 然后对 document.createElement 创建的 stylescript 返回的元素打上 Symbol 标记。
  • 其次对 HTMLHeadElementHTMLBodyElement 的方法进行劫持(appendChild / removeChild / insertBefore),保存动态添加的元素作标记。当传入的元素是 script 且有 src 属性则请求该资源并加载,触发 CustomEvent('load'),其他场景处理省略。
  • 通过上述操作,可知 umijs/qiankun 通过 Proxy 代理 window 下的属性和劫持原生 dom 添加、插入等方法对动态添加的元素、当前应用的操作进行记录,当应用切换时,可将记录的操作进行还原,从而实现了 JS 沙箱机制。
  • 对于 CSS 资源,通过添加唯一前缀选择器的操作用来标记该样式属于当前应用。从而实现了 CSS 隔离方案。

单个页面同时加载不同模块(技术框架)的消息共享

对于 A B 两个模块同时出现在同个页面,由于技术栈的限制,它们之间的通信只能借助 CustomEventdocument.addEventListener('message', xxx); 另一种可能是,如果 A B 模块使用的框架都支持 Web Components,可以转为 Web Components 再进行通信,这时,相当于借助了一个浏览器支持的第三方框架来抹平不同框架间的差距,进而实现消息共享。但不同框架需要做对 Web Components 的转换方案。