基于 Web Components 的微前端框架(中)

micro-app 是京东零售推出的一款微前端框架,它基于类 WebComponent 进行渲染,从组件化的思维实现微前端,旨在降低上手难度、提升工作效率。它是目前接入微前端成本最低的框架,并且提供了JS沙箱、样式隔离、元素隔离、预加载、资源地址补全、插件系统、数据通信等一系列完善的功能。

组件微前端

微前端的核心在于资源加载与渲染,传统的 iframe 的渲染方式就是一个典型,只要能够实现一种元素隔离的功能并且路由符合要求,子应用理论上不需要修改代码就可以嵌入另外一个页面渲染,micro-app 探索不一样的实现思路。

使用类WebComponent + HTML Entry 的思路。

HTML Entry

HTML Entry 是指设置 html 作为资源入口,通过加载远程 html,解析其 DOM 结构从而获取 js、css等静态资源来实现微前端的渲染,这也是 qiankun 目前采用的渲染方案。

Web Components

Web Components 是一组 Web 平台 API,建立在 Web 标准之上,它允许开发人员创建新的自定义、可重用、被封装的 HTML 标记在网页和 Web 应用程序中使用。 HTML 标记基于 Web Components 标准构建,可跨现代浏览器工作,并可与任何支持 HTML 的 JavaScript 库或框架(Vue、React、Angular... )一起使用。

Web Components 不是一门单一的技术,而是四门技术的组合,这四门技术分别是:

  • HTML Imports(被废弃,被 ES Modules 取而代之)
  • Custom Elements
  • Shadow DOM
  • HTML templates 它有两个核心组成部分:CustomElement 和 ShadowDom。而类WebComponent就是使用 CustomElement 结合 ShadowDom 实现 Web Components 基本一致的功能。这里简单了解一下这两个核心。

CustomElement

从名字来看,就知道 Custom Elements 是用来创建自定义 HTML 标签。Cumtom elements 这个概念对于写过 Vue、React、Angular 的开发者而言应该非常的熟悉,在框架中通过组件的形式,使用自己定义的标签。就拿 Vue 来举例:

js 复制代码
// Custom.vue
<template>
  <p>大家好,我是拜小白,我是一个自定义标签。</p>
</template>
js 复制代码
<template>
  <div>
    <x-custom></x-custom>
    <p>你好,{{ name }},我知道你是一个自定义标签!</p>
  </div>
</template>

<script>
import Custom from "./Custom";
module.exports = {
  data: function () {
    return {
      name: "拜小白",
    };
  },
  components: {
    "x-custom": Custom,
  },
};
</script>

Shadow DOM

Shadow DOM 其实对于大家来说既陌生又熟悉,我相信你一定见过,在某些 HTML 元素下,有一段 #shadow-root 代码块,这里的 #shadow-root 所包含的内容其实就是所谓的 shadow-dom 。 例如:<video> 标签

注意:浏览器默认是看不到 #shadow-root,需要自己将 #shadow-root 设置为可见。

html 复制代码
<!DOCTYPE html>
<html>
  <head> 
 		 <meta charset="utf-8"> 
  </head>
  <body>
  
  <video width="320" height="240" controls autoplay>
    <source src="movie.ogg" type="video/ogg">
    <source src="movie.mp4" type="video/mp4">
    <source src="movie.webm" type="video/webm">
    <object data="movie.mp4" width="320" height="240">
      <embed width="320" height="240" src="movie.swf">
    </object>
  </video>
  </body>
</html>

当实际在浏览器进行查看时,你就会发现,Dom 结构并非代码看到的这么简单。 这些操作的关键在于,Shadow DOM,它可以将一个隐藏的、独立的 DOM 附加到一个元素上。Shadow DOM 允许将隐藏的 DOM 树附加到常规的 DOM 树中------它以 shadow root 节点为起始根节点,在这个根节点的下方,可以是任意元素,和普通的 DOM 元素一样。

ShadowDom 用于创建阴影 DOM,阴影 DOM 具有天然的样式隔离和元素隔离属性。理论上是实现微前端最优的方案。但是 ShadowDom 的兼容性非常不好,一些前端框架在 ShadowDom 环境下无法正常运行,尤其是 react 框架。

由于 ShadowDom 存在的问题,所以 micro-app 采用自定义的样式隔离和元素隔离实现 ShadowDom 类似的功能,然后将微前端应用封装在一个 CustomElement 中,从而模拟实现了一个 类 WebComponent组件,它的使用方式和兼容性与 WebComponent 一致,同时也避开了ShadowDom 的问题。

并且由于自定义 ShadowDom 的隔离特性,Micro App 不需要像 single-spa 和 qiankun 一样要求子应用修改渲染逻辑并暴露出方法,也不需要修改 webpack 配置。

MicroApp 的核心功能在 CustomElement 基础上进行构建

micro-app 的 CustomElement 用于创建自定义标签,并提供了元素的渲染、卸载、属性修改等钩子函数,通过钩子函数获知微应用的渲染时机,并将自定义标签作为容器,微应用的所有元素和样式作用域都无法逃离容器边界,从而形成一个封闭的环境。 micro-app 通过 CustomElement 的生命周期函数 connectedCallback 监听元素被渲染。

Web Components 有属于自己的生命周期钩子函数,当我们定义一个元素时,它会在元素的不同阶段触发它们。

  • connectedCallback:当 custom element 首次被插入文档 DOM 时,被调用。
  • disconnectedCallback:当 custom element 从文档 DOM 中删除时,被调用。
  • adoptedCallback:当 custom element 被移动到新的文档时,被调用。
  • attributeChangedCallback: 当 custom element 增加、删除、修改自身属性时,被调用。

micro-app中,当加载子应用的 html 时,将 html 转换为 dom 结构,这样可以拿到 js 和 css 资源。拦截所有动态创建的 script、link 等标签,提取标签内容。将加载的 js 经过插件系统处理后放入沙箱中运行,对 css 资源进行样式隔离,最后将格式化后的元素放入 micro-app中,最终将micro-app元素渲染为一个微前端的子应用。在渲染的过程中,会执行开发者绑定的生命周期函数,用于进一步操作。

MicroApp 的元素隔离

MicroApp 模拟实现了 ShadowDom 的功能,拦截了底层原型链上元素的方法,保证子应用只能对自己内部的元素进行操作,每个子应用都有自己的元素作用域。 在生成的的节点中,micro-app 就是通过 CustomElement 实现的自定义节点,在这个自定义节点下还包含了两部分micro-app-headmicro-app-body,分别对应 html 中的 head 和 body 元素。 head 中包含了动态创建 link、script,body 包含了原 body 元素中的内容。这样做的好处是可以防止子应用的元素泄漏到全局,在进行元素查询、删除等操作时,只需要在micro-app内部进行处理,是实现元素隔离的重要基础。 可以将 micro-app 理解为一个内嵌的 html 页面,它的结构和功能都和 html 页面类似。

小结

micro-app 将所有功能都封装到一个类 WebComponent 组件中,从而实现在基座应用中嵌入一行代码即可渲染一个微前端应用。micro-app并没有沿袭 single-spa 的思路,而是借鉴了 WebComponent 的思想,通过 CustomElement 结合自定义的 ShadowDom,将微前端封装成一个类 WebComponent 组件,从而实现微前端的组件化渲染。并且由于自定义 ShadowDom 的隔离特性,micro-app 不需要像 single-spa 和 qiankun 一样要求子应用修改渲染逻辑并暴露出方法,也不需要修改 webpack 配置,是目前市面上接入微前端成本最低的方案。

参考

相关推荐
也无晴也无风雨1 小时前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
Martin -Tang1 小时前
Vue 3 中,ref 和 reactive的区别
前端·javascript·vue.js
FakeOccupational3 小时前
nodejs 020: React语法规则 props和state
前端·javascript·react.js
放逐者-保持本心,方可放逐3 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架
曹天骄4 小时前
next中服务端组件共享接口数据
前端·javascript·react.js
阮少年、4 小时前
java后台生成模拟聊天截图并返回给前端
java·开发语言·前端
郝晨妤6 小时前
鸿蒙ArkTS和TS有什么区别?
前端·javascript·typescript·鸿蒙
AvatarGiser6 小时前
《ElementPlus 与 ElementUI 差异集合》Icon 图标 More 差异说明
前端·vue.js·elementui
喝旺仔la6 小时前
vue的样式知识点
前端·javascript·vue.js
别忘了微笑_cuicui6 小时前
elementUI中2个日期组件实现开始时间、结束时间(禁用日期面板、控制开始时间不能超过结束时间的时分秒)实现方案
前端·javascript·elementui