阅读 312

微前端框架MicroApp

什么是微前端

微前端是一种类似于微服务的架构,是一种由独立交付的多个前端应用组成整体的架构风格,将前端应用分解成一些更小、更简单的能够独立开发、测试、部署的应用,而在用户看来仍然是内聚的单个产品。简单来说就是一个项目里可以内嵌多个子项目,子项目可以单独开发、部署并且技术框架无关。

使用场景

  • 大规模企业级 Web 应用开发

  • 跨团队及企业级应用协作开发

  • 不知道大家有没有经历过俗称屎山的项目,就是用的技术比较老然后还堆积了很多业务要重构需要花很大成本, 我觉得这种场景就很适合使用微前端去解决。

MicroApp介绍

micro-app之前,业内已经有一些开源的微前端框架,比较流行的有2个:single-spaqiankun single-spa是通过监听 url change 事件,在路由变化时匹配到渲染的子应用并进行渲染,这个思路也是目前实现微前端的主流方式。同时single-spa要求子应用修改渲染逻辑并暴露出三个方法:bootstrapmountunmount,分别对应初始化、渲染和卸载,这也导致子应用需要对入口文件进行修改。因为qiankun是基于single-spa进行封装,所以这些特点也被qiankun继承下来,并且需要对webpack配置进行一些修改。

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

micro-app将所有功能都封装到一个类WebComponent组件中,从而实现在基座应用中嵌入一行代码即可渲染一个微前端应用。 同时micro-app还提供了js沙箱样式隔离元素隔离预加载数据通信静态资源补全插件系统一系列完善的功能。

micro-app兼容所有框架以及脚手架构建工具包括ViteNextjsNuxtjs

WebComponent基本概念

WebComponent就是利用官方提供的原生API实现类似Vue、React的组件形式,它由三项主要技术组成,它们可以一起使用来创建封装功能的定制元素,可以在你喜欢的任何地方重用,不必担心代码冲突。

  • Custom elements(自定义元素): 一组JavaScript API,允许您定义custom elements及其行为,然后可以在您的用户界面中按照需要使用它们。

  • Shadow DOM(影子DOM) :一组JavaScript API,用于将封装的“影子”DOM树附加到元素(与主文档DOM分开呈现)并控制其关联的功能。通过这种方式,您可以保持元素的功能私有,这样它们就可以被脚本化和样式化,而不用担心与文档的其他部分发生冲突。

  • HTML templates(HTML模板):  <template> 和 <slot> 元素使您可以编写不在呈现页面中显示的标记模板。然后它们可以作为自定义元素结构的基础被多次重用。

写一个简单的组件

  • 定义自定组件:

    class MyButton extends HTMLElement {     constructor () {         super();                 // 创建一个 shadow root         const shadow = this.attachShadow({mode: 'open'});         const template = document.getElementById('mybutton');         const content = template.content.cloneNode(true);         shadow.appendChild(content);     }     // 生命周期     // connectedCallback:当 custom element首次被插入文档DOM时,被调用。     // disconnectedCallback:当 custom element从文档DOM中删除时,被调用。     // adoptedCallback:当 custom element被移动到新的文档时,被调用。     // attributeChangedCallback: 当 custom element增加、删除、修改自身属性时,被调用。       connectedCallback() {         console.log('Custom square element added to page.');         updateStyle(this);       }        disconnectedCallback() {          console.log('Custom square element removed from page.');        }                 adoptedCallback() {          console.log('Custom square element moved to new page.');        }                 attributeChangedCallback(name, oldValue, newValue) {          console.log('Custom square element attributes changed.');          updateStyle(this);        } } 复制代码

  • 定义组件模板:

    <template id="mybutton">   <style>     button {       color: white;       background-color: #666;       padding: 5px;     }   </style>     <button>Add</button> </template> 复制代码

  • 注册组件:

    window.customElements.define('my-button', MyButton); 复制代码

  • 使用组件:

    <body>     <my-button></my-button> </body> 复制代码

核心原理

MicroApp 的核心功能在CustomElement基础上进行构建,CustomElement用于创建自定义标签,并提供了元素的渲染、卸载、属性修改等钩子函数,我们通过钩子函数获知微应用的渲染时机,并将自定义标签作为容器,微应用的所有元素和样式作用域都无法逃离容器边界,从而形成一个封闭的环境。

概念图&对比图

image.png

image.png

基本使用

下面基座应用以React为例

基座应用

每个自定义标签micro-app渲染后就是一个微前端的子应用,它的使用方式类似于iframe标签。 我们需要给标签传递三个基础属性:

  • name:名称

  • url:子应用页面地址

  • baseurl:baseurl是基座应用分配给子应用的路由前缀

// router.js  import { BrowserRouter, Switch, Route } from 'react-router-dom'  import ChildPage from './child-page'  export default function AppRoute () {          return (              <BrowserRouter>                  <Switch>                      // 非严格匹配,/child/* 都将匹配到ChildPage组件                      // /child 就是分配给子应用的路由前缀baseroute                      <Route path='/child'> <ChildPage /> </Route>                  </Switch>              </BrowserRouter>  ) }  export function ChildPage () {          return (              <div>                  <h1>子应用</h1>                  <micro-app                      name='child-app'                      url='http://localhost:3000/'                      baseroute='/child'                     // 生命周期                     onCreated={() => console.log('micro-app元素被创建')}                      onBeforemount={() => console.log('即将被渲染')}                      onMounted={() => console.log('已经渲染完成')}                      onUnmount={() => console.log('已经卸载')}                      onError={() => console.log('渲染出错')}>                 </micro-app>              </div>            )} 复制代码

子应用

如果子应用是多页面,只需要修改路由配置,添加路由前缀。 如下: window.__MICRO_APP_BASE_URL__是由基座应用下发的路由前缀,在非微前端环境下,这个值为undefined

React
import { BrowserRouter, Switch, Route } from 'react-router-dom' export default function AppRoute () {   return (     <BrowserRouter basename={window.__MICRO_APP_BASE_ROUTE__ || '/'}>       <Switch>         ...       </Switch>     </BrowserRouter>   ) } 复制代码

Vue2
// main.js import VueRouter from 'vue-router' import routes from './router' const router = new VueRouter({   mode: 'history',   // ???? __MICRO_APP_BASE_ROUTE__ 为micro-app传入的基础路由   base: window.__MICRO_APP_BASE_ROUTE__ || process.env.BASE_URL,   routes, }) 复制代码

完整案例:github.com/micro-zoe/m…

插件系统

微前端的使用场景都是比较复杂所以坑会比较多,MicroApp提供了一套插件系统,每一个js文件都会经过插件系统,我们可以利用插件系统对这些js做一些特殊处理。

image.png


作者:直男
链接:https://juejin.cn/post/7054099370416799751


文章分类
前端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐