探探各个微前端框架

本文作者为 360 奇舞团前端开发工程师

微前端架构是为了在解决单体应用在一个相对长的时间跨度下,由于参与的人员、团队的增多、变迁,从一个普通应用演变成一个巨石应用(Frontend Monolith)后,随之而来的应用不可维护的问题。这类问题在企业级 Web 应用中尤其常见。
微前端框架内的各个应用都支持独立开发部署、不限技术框架、支持独立运行、应用状态隔离但也可共享等特征。
本文会从框架的应用隔离实现方案、实战、优缺点三个方面探一探各个框架。帮助大家了解各个框架是如何使用,如何运行,从而能选出适合自己项目的微前端方案。

iframe

在没有各大微前端解决方案之前,iframe是解决这类问题的不二之选,因为iframe提供了浏览器原生的硬隔离方案,不论是样式隔离、js 隔离这类问题统统都能被完美解决。
但他的最大问题也在于他的隔离性无法被突破,导致应用间上下文无法被共享,随之带来的开发体验、产品体验的问题:

  1. url 不同步,浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用。

  2. UI 不同步,DOM 结构不共享,弹窗只能在iframe内部展示,无法覆盖全局

  3. 全局上下文完全隔离,内存变量不共享,iframe 内外系统的通信、数据同步等需求,主应用的 cookie 要透传到根域名都不同的子应用中实现免登效果。

  4. ,每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程。

single-spa

目前(2024年4月)github star 13k
Single-spa(https://github.com/single-spa/single-spa) 是最早的微前端框架,兼容多种前端技术栈;是一个将多个单页面应用聚合为一个整体应用的 JavaScript 微前端框架;
简单来说就是一个聚合,使用这个库可以让你的应用可以 使用多个不同的技术栈(vue、react、angular等等)进行同步开发,最后使用一个公用的路由去实现完美的切换;

实现方案

Single-spa 实现了一套生命周期,开发者需要在相应的时机自己去加载对应的子应用。
它做的事情就是注册子应用、监听 URL 变化,然后加载对应的子应用js,执行对应子应用的生命周期流程。

  1. 提供registerApplication方法,用来注册子应用列表。

  2. 提供了activeWhen,由开发者指定路由满足条件时,激活(挂载)子应用的js、css。

  3. js隔离single-spa-leaked-globals实现,本质上就是在 mount A 子应用时,正常添加全局变量,比如 jQuery 的 $, lodash 的 _。在 unmount A 子应用时,用一个对象记录之前给 window 添加的全局变量,并把 A 应用里添加 window 的变量都删掉。下一次再 mount A 应用时,把记录的全局变量重新加回来就好了。

  4. css隔离主应用和子应用之间通过single-spa-css插件提供的css生命周期函数,做到子应用mount时加载css,子应用unmount时将css也unmount掉;而子应用与子应用之间可以通过PostCSSPrefix Selector 给样式自动加前缀的方式,或者Shadow DOM 的形式去解决。

single-spa实战

1. 主应用入口文件:

主要通过single-spa提供的registerApplication方法注册子应用,子应用需要指定加载子应用的方法、和路由条件。

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import { registerApplication, start } from 'single-spa'Vue.config.productionTip = false// 远程加载子应用
function createScript(url) {return new Promise((resolve, reject) => {const script = document.createElement('script')script.src = urlscript.onload = resolvescript.onerror = rejectconst firstScript = document.getElementsByTagName('script')[0]firstScript.parentNode.insertBefore(script, firstScript)})
}// 记载函数,返回一个 promise
function loadApp(url, globalVar) {// 支持远程加载子应用return async () => {await createScript(url + '/js/chunk-vendors.js')await createScript(url + '/js/app.js')// 这里的return很重要,需要从这个全局对象中拿到子应用暴露出来的生命周期函数return window[globalVar]}
}// 子应用列表
const apps = [{// 子应用名称name: 'app1',// 子应用加载函数,是一个promiseapp: loadApp('http://localhost:8081', 'app1'),// 当路由满足条件时(返回true),激活(挂载)子应用activeWhen: location => location.pathname.startsWith('/app1'),// 传递给子应用的对象customProps: {}},{name: 'app2',app: loadApp('http://localhost:8082', 'app2'),activeWhen: location => location.pathname.startsWith('/app2'),customProps: {}},{// 子应用名称name: 'app3',// 子应用加载函数,是一个promiseapp: loadApp('http://localhost:3000', 'app3'),// 当路由满足条件时(返回true),激活(挂载)子应用activeWhen: location => location.pathname.startsWith('/app3'),// 传递给子应用的对象,这个很重要,该配置告诉react子应用自己的容器元素是什么,这块儿和vue子应用的集成不一样,官网并没有说这部分,或者我没找到,是通过看single-spa-react源码知道的customProps: {domElement: document.getElementById('microApp'),// 添加 name 属性是为了兼容自己写的lyn-single-spa,原生的不需要,当然加了也不影响name: 'app3'}}
]// 注册子应用
for (let i = apps.length - 1; i >= 0; i--) {registerApplication(apps[i])
}new Vue({router,mounted() {// 启动start()},render: h => h(App)
}).$mount('#app')
2. 子应用导出文件
  • 子应用需要安装single-spa-react或者single-spa-vue

  • 将子应用传递给single-spa-react,得到子应用运行的生命周期,

  • 子应用将生命周期导出到全局,

  • 在主应用可以获取子应用的生命周期函数

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css'
import { BrowserRouter, Link, Route } from 'react-router-dom'
import singleSpaReact from 'single-spa-react'// 子应用独立运行
if (!window.singleSpaNavigate) {ReactDOM.render(rootComponent(), document.getElementById('root'))
}// 生命周期a
const reactLifecycles = singleSpaReact({React,ReactDOM,rootComponent,errorBoundary(err, info, props) {return <div>This renders when a catastrophic error occurs</div>}
})// 这里和vue不一样,props必须向下传递
export const bootstrap = async props => {console.log('app3 bootstrap');return reactLifecycles.bootstrap(props)
}
export const mount = async props => {console.log('app3 mount');return reactLifecycles.mount(props);
}
export const unmount = async props => {console.log('app3 unmount');return reactLifecycles.unmount(props)
}// 根组件
function rootComponent() {return <React.StrictMode><BrowserRouter><div><Link to="/app3">Home</Link> |<Link to="/app3/about"> About</Link><Route exact path="/app3" component={Home} /><Route exact path="/app3/about" component={About} /></div></BrowserRouter></React.StrictMode>
}// home 组件
function Home() {return <div><h1>app3 home page</h1></div>
}// about 组件
function About() {return <div><h1>app3 about page</h1></div>
}
3. 打包配置

将子应用导出模式设置为umd

const package = require('./package.json')
module.exports = {// 告诉子应用在这个地址加载静态资源,否则会去基座应用的域名下加载publicPath: '//localhost:8082',// 开发服务器devServer: {port: 8082},configureWebpack: {// 导出umd格式的包,在全局对象上挂载属性package.name,基座应用需要通过这个全局对象获取一些信息,比如子应用导出的生命周期函数output: {// library的值在所有子应用中需要唯一library: package.name,libraryTarget: 'umd'}}
}
4. 预览

57376769d532b0725dc8ffc24aa0f00c.png
可以看到它是动态加载的子应用的js,并执行js,将内容渲染到了主应用的盒子内。

框架优缺点

优点:
  • 敏捷性 - 独立开发、独立部署,微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新;

  • 技术栈无关,主框架不限制接入应用的技术栈,微应用具备完全自主权;

  • 增量升级,在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略

缺点
  • 需要自己去加载子应用

  • 不支持 Javascript 沙箱隔离,需要自己去使用single-spa-leaked-globals之类的库去隔离

  • 不支持css隔离,需要自己使用single-spa-css库或者postcss等去解决样式冲突问题

  • 无法预加载

qiankun

目前(2024年4月) github star 15.4k
阿里的qiankun 是一个基于 single-spa 的微前端实现库,孵化自蚂蚁金融,帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。

实现方案

  • single-spa是基于js-entry方案,而qiankun 是基于html-entry 及沙箱设计,使得微应用的接入 像使用 iframe 一样简单

  • 主应用监听路由,加载对应子应用的html,挂载到主应用的元素内,然后解析子应用的html,从中分析出css、js再去沙盒化后加载执行,最终将子应用的内容渲染出来。

  • qiankun实现样式隔离有两种模式可供开发者选择:

    • strictStyleIsolation 这种模式下 qiankun 会为每个微应用的容器包裹上一个 shadow dom 节点,从而确保微应用的样式不会对全局造成影响。

    • experimentalStyleIsolation 当 experimentalStyleIsolation 被设置为 true 时,qiankun 会改写子应用所添加的样式,会为所有样式规则增加一个特殊的选择器规则,来限定其影响范围

  • qiankun实现js隔离,采用了两种沙箱,分别为基于Proxy实现的沙箱快照沙箱,当浏览器不支持Proxy会降级为快照沙箱

Proxy沙箱机制:
// 伪代码
class ProxySandbox {constructor() {const rawWindow = window;const fakeWindow = {}const proxy = new Proxy(fakeWindow, {set(target, p, value) {target[p] = value;return true},get(target, p) {return target[p] || rawWindow[p];}});this.proxy = proxy}
}
let sandbox1 = new ProxySandbox();
let sandbox2 = new ProxySandbox();
window.a = 1;
// 伪代码
((window) => {window.a = 'hello';console.log(window.a) // hello
})(sandbox1.proxy);
((window) => {window.a = 'world';console.log(window.a) // world
})(sandbox2.proxy);
快照沙箱
// 伪代码
class SnapshotSandbox {constructor() {this.proxy = window; this.modifyPropsMap = {}; // 修改了那些属性this.active(); // 调用active保存主应用window快照}/**1. 初始化时,在子应用即将mount前,先调用active,保存当前主应用的window快照*/active() {this.windowSnapshot = {}; // window对象的快照for (const prop in window) {if (window.hasOwnProperty(prop)) {// 将window上的属性进行拍照this.windowSnapshot[prop] = window[prop];}}Object.keys(this.modifyPropsMap).forEach(p => {window[p] = this.modifyPropsMap[p];});}/*** 子应用卸载时,遍历当前子应用的window属性,和主应用的window快照做对比* 如果不一致,做两步操作 *     1. 保存 不一致的window属性,*     2. 还原window*/inactive() {for (const prop in window) { // diff 差异if (window.hasOwnProperty(prop)) {// 将上次拍照的结果和本次window属性做对比if (window[prop] !== this.windowSnapshot[prop]) {// 保存修改后的结果this.modifyPropsMap[prop] = window[prop]; // 还原windowwindow[prop] = this.windowSnapshot[prop]; }}}}
}

qiankun实战

1. 主应用入口文件
初始化主应用,并注册子应用

主应用入口文件初始化应用,注册子应用,注册子应用时支持传入子应用列表, 注册子应用时需要指明以下几个主要参数:

  1. name: 微应用的名称,微应用之间必须确保唯一

  2. entry: 子应用的访问链接。主应用会加载整个页面,例如https://qiankun.umijs.org/guide/

  3. container:需要挂载子应用的DOM元素

  4. loader: 子应用未加载时的界面,一般为loading

  5. activeRule: 路由匹配规则

开启子应用start(options)

options.prefetch此时可以选择是否预加载子应用。
options.sandbox默认情况下的沙箱可以确保单实例场景子应用之间的样式隔离,但是无法确保主应用跟子应用、或者多实例场景的子应用样式隔离。qiankun提供了另外两种方式的隔离,供开发者选择:

  • strictStyleIsolation: 当配置为 { strictStyleIsolation: true } 时表示开启严格的样式隔离模式。这种模式下 qiankun 会为每个微应用的容器包裹上一个 shadow dom 节点,从而确保微应用的样式不会对全局造成影响。

  • experimentalStyleIsolation:当 {experimentalStyleIsolation: true} 被设置,qiankun 会改写子应用所添加的样式为所有样式规则增加一个特殊的选择器规则来限定其影响范围。

import { registerMicroApps, start, initGlobalState } from 'qiankun';registerMicroApps([{name: 'react app', // app name registeredentry: '//localhost:7100',container: '#yourContainer',activeRule: '/yourActiveRule',},{name: 'vue app',entry: { scripts: ['//localhost:7100/main.js'] },container: '#yourContainer2',activeRule: '/yourActiveRule2',},
]);// 通讯
const { onGlobalStateChange, setGlobalState } = initGlobalState({user: 'qiankun',
});onGlobalStateChange((value, prev) => console.log('[onGlobalStateChange - master]:', value, prev));setGlobalState({ignore: 'master',user: {name: 'master',},
});/*** 设置默认进入的子应用*/
setDefaultMountApp('/react16');/*** 启动应用*/
start({prefetch: true, // 预加载子应用sandbox:{strictStyleIsolation: true, //  shadow dom的方式实现样式隔离// experimentalStyleIsolation: true, //添加特殊的选择器的方式实现样式隔离}
});runAfterFirstMounted(() => {console.log('[MainApp] first app mounted');
});
2. 子应用导出生命周期钩子

子应用需要在自己的入口 js导出 bootstrap、mount、unmount 三个生命周期钩子,以供主应用在适当的时机调用。

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';function render(props) {const { container } = props;ReactDOM.render(<App />, container ? container.querySelector('#root') : document.querySelector('#root'));
}/**
* 和主应用通讯
*/
function storeTest(props) {props.onGlobalStateChange((value, prev) => console.log(`[onGlobalStateChange - ${props.name}]:`, value, prev), true);props.setGlobalState({ignore: props.name,user: {name: props.name,},});
}if (!window.__POWERED_BY_QIANKUN__) {render({});
}/*** bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。*/
export async function bootstrap() {console.log('[react16] react app bootstraped');
}/*** 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法*/
export async function mount(props) {console.log('[react16] props from main framework', props);storeTest(props);render(props);
}/*** 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例*/
export async function unmount(props) {const { container } = props;ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root'));
}
3. 配置打包工具:

为了让主应用能正确识别微应用暴露出来的一些全局信息和开发环境下的跨域兼容,在子应用(以create-react-app出来的react项目为例)安装@rescripts/cli,并在子应用目录下新建.rescriptsrc.js,内容如下:

const { name } = require('./package');module.exports = {webpack: (config) => {config.output.library = `${name}-[name]`;config.output.libraryTarget = 'umd'; // 为了能通过window['app-name1']拿到子应用声明的生命周期// webpack 5 需要把 jsonpFunction 替换成 chunkLoadingGlobalconfig.output.jsonpFunction = `webpackJsonp_${name}`; config.output.globalObject = 'window';return config;},devServer: (_) => {const config = _;config.headers = {'Access-Control-Allow-Origin': '*',};config.historyApiFallback = true;config.hot = false;config.watchContentBase = false;config.liveReload = false;return config;},
};
4. 预览

使用strictStyleIsolation:true方式进行样式隔离,会生成一个shadow dom,进行样式的完全隔离:8a60adfa335fc1f446a625be8614c71b.png

使用experimentalStyleIsolation:true的方式进行样式隔离,会在css选择器前添加特殊标识:
cdbd6ba7c9421e9f1a2b58eba6c892b3.png

可以看到,qiankun会将子应用的html渲染到自定义的container中。 主应用加载的是子应用的html,在解析子应用的html的过程中遇到js和css会载框架内进行沙盒处理,完成css和js的隔离,之后下载并执行,完成整个子应用的渲染过程。

框架优缺点

优点
  • html entry的接入方式,不需要自己写load方法,而是直接写子应用的访问链接就可以。

  • 提供js沙箱

  • 提供样式隔离,两种方式可选

  • 资源预加载,在浏览器空闲时间预加载未打开的微应用资源,加速微应用打开速度。

  • 社区活跃

  • umi 插件,提供了 @umijs/plugin-qiankun 供 umi 应用一键切换成微前端架构系统 除了最后一点拓展以外,微前端想要达到的效果都已经达到。

  • 应用间通信简单,全局注入

  • 路由保持,浏览器刷新、前进、后退,都可以作用到子应用

缺点
  • 改造成本较大,从 webpack、代码、路由等等都要做一系列的适配

  • 对 eval 的争议,eval函数的安全和性能是有一些争议的:MDN的eval介绍;

  • 无法同时激活多个子应用,也不支持子应用保活

  • 无法支持 vite 等 ESM 脚本运行

wujie

目前(2024年4月)github star 3.7k
wujie是腾讯出品。基于 webcomponent 容器 + iframe 沙箱,能够完善的解决适配成本、样式隔离、运行性能、页面白屏、子应用通信、子应用保活、多应用激活、vite 框架支持、应用共享等

实现方案

  • 无界利用iframewebcomponent来搭建天然的js隔离沙箱css隔离沙箱,

  • 利用iframe的history主应用的history在同一个top-level browsing context来搭建天然的路由同步机制

  • 支持以fiber的形式执行js,由于子应用的执行会阻塞主应用的渲染线程,当fiber设置为true,那么js执行时采取类似react fiber的模式方式间断执行,每个 js 文件的执行都包裹在requestidlecallback中,每执行一个js可以返回响应外部的输入,但是这个颗粒度是js文件,如果子应用单个js文件过大,可以通过拆包的方式降低达到fiber模式效益最大化

wujie是如何渲染子应用的?

wujie跟qiankun一样,都是基于html entry加载的,但他们解析html的过程是不一样的。 qiankun是直接解析并执行js、css、html的,而wujie则是先解析html,提取出script脚本放入空的iframe中,提取出css、html放入到web components中,具体来说:

  1. 解析入口 HTML ,分别得到script、css、模版html

  2. 创建一个纯净的 iframe,为了实现应用间(iframe 间)通讯,无界子应用 iframe 的 url 会设置为主应用的域名(同域),因此 iframe 的 location.href 并不是子应用的 url。创建好后停止加载iframe。

  3. iframe内插入js,将抽离出来的script脚本,插到iframe中去,在iframe中执行子应用的js

  4. 创建web component,id为子应用id,将抽离出来的html插入。

  5. 由于iframe内的js有可能操作dom,但是iframe内没有dom,随意wujie框架内对iframe拦截document对象,统一将dom指向shadowRoot,此时比如新建元素、弹窗或者冒泡组件就可以正常约束在shadowRoot内部。

wujie实战

wujie接入很简单,主应用可以让开发者以组件的方式加载子应用。子应用只需要做支持跨域请求改造,这个是所有微前端框架运行的前提,除此之外子应用可以不做任何改造就可以在无界框架中运行,不过此时运行的方式是重建模式。 子应用也可以配置保活、生命周期适配进入保活模式或单例模式。

1. 主应用入口文件

与其他框架一样,先配置子应用,

// main-react/index.js
import "react-app-polyfill/stable";
import "react-app-polyfill/ie11";import React from "react";
import ReactDOM from "react-dom";
import WujieReact from "wujie-react";
import "./index.css";
import App from "./App";
import hostMap from "./hostMap";
import credentialsFetch from "./fetch";
import lifecycles from "./lifecycle";
import plugins from "./plugin";const { setupApp, preloadApp, bus } = WujieReact;
const isProduction = process.env.NODE_ENV === "production";
bus.$on("click", (msg) => window.alert(msg));const degrade = window.localStorage.getItem("degrade") === "true" || !window.Proxy || !window.CustomElementRegistry;
/*** 大部分业务无需设置 attrs* 此处修正 iframe 的 src,是防止github pages csp报错* 因为默认是只有 host+port,没有携带路径*/
const attrs = isProduction ? { src: hostMap("//localhost:7700/") } : {};
/*** 配置应用,主要是设置默认配置* preloadApp、startApp的配置会基于这个配置做覆盖*/
setupApp({name: "react16",url: hostMap("//localhost:7600/"),attrs, // 子应用iframe的srcexec: true, // 预执行fetch: credentialsFetch, // 自定义的fetch方法plugins,/** 子应用短路径替换,路由同步时生效 */prefix: { "prefix-dialog": "/dialog", "prefix-location": "/location" },/** 子应用采用降级iframe方案 */degrade,...lifecycles,
});setupApp({name: "vue3",url: hostMap("//localhost:7300/"),attrs,exec: true,alive: true, // 子应用保活,state不会丢失plugins: [{ cssExcludes: ["https://stackpath.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"] }],// 引入了的第三方样式不需要添加credentialsfetch: (url, options) =>url.includes(hostMap("//localhost:7300/")) ? credentialsFetch(url, options) : window.fetch(url, options),degrade,...lifecycles,
});if (window.localStorage.getItem("preload") !== "false") {preloadApp({name: "react16",});if (window.Proxy) {preloadApp({name: "vue3",});}
}ReactDOM.render(<App />, document.getElementById("root"));

引入子应用的地方直接以组件式的方式引入:

import React from "react";
import hostMap from "../hostMap";
import WujieReact from "wujie-react";
import { useNavigate, useLocation } from "react-router-dom";export default function React16() {const navigation = useNavigate();const location = useLocation();const path = location.pathname.replace("/react16-sub", "").replace("/react16", "").replace("/",""); const react16Url = hostMap("//localhost:7600/") + path;const props = {jump: (name) => {navigation(`/${name}`);},};return (// 单例模式,name相同则复用一个无界实例,改变url则子应用重新渲染实例到对应路由<WujieReactwidth="100%"height="100%"name="react16"url={react16Url}sync={!path}props={props}></WujieReact>);
}
2. 预览

1ed7c75049d924e4f636c5e707473df5.png

框架优缺点

优点
  • 接入简单,可以以组件的方式引入子应用

  • 纯净无污染

    • 无界利用iframewebcomponent来搭建天然的js隔离沙箱和css隔离沙箱

    • 利用iframe的history和主应用的history在同一个top-level browsing context来搭建天然的路由同步机制

    • 副作用局限在沙箱内部,子应用切换无需任何清理工作,没有额外的切换成本

  • 支持vite esmoudle加载,由于js是独立在iframe中加载的,所以支持esmodule加载

  • 支持预加载

  • 支持应用保活,子应用状态保留,由于是独立在iframe中的,而切换应用时不会移除iframe,所以子应用的状态会被保留在原来的iframe中,当主应用再次渲染子应用dom时,会显示之前的状态。

  • 多应用同时激活在线

缺点
  1. iframe沙箱的src设置了主应用的host,初始化iframe的时候需要等待iframe的location.orign从'about:blank'初始化为主应用的host,这个采用的计时器去等待的不是很优雅。

Micro App

截至目前(2024年4月)github star 5.2k
mirco-app 是京东2021年开源的一款微前端框架。它借助了浏览器对 webComponent 的支持,实现了一套微前端方案体系。并且由于 Shadow Dom 对 react 这类库的兼容性较差,便自己实现了类 Shadow Dom 的效果。与 qiankun 相比,接入更加简单。最新的版本也支持iframe实现js隔离,类似wujie

实现方案

首先micro-app实现了一个基于WebComponent的组件,并实现了类Shadow Dom 的效果,开发者只需要用<micro-app name="xx" url="xx" baseroute="/xxx/xxx">来加载子应用,整个对子应用的加载、js隔离、css隔离的逻辑都封装在了web component组件<micro-app>中,具体来说:

  1. 当调用microApp.start()后,会注册一个名为micro-app 的自定义 webComponent 标签。我们可以从 <micro-app name='app1' url='xx' baseroute='/my-page'></micro-app> 中拿到子应用的线上入口地址。

  2. <micro-app>组件内部,当匹配到路由后,跟qiankun一样加载html,得到html字符串模版

  3. 分析html字符串,提取<head>头和<body>,并替换为框架自定义标签<micro-app-head><micro-app-body>

  4. <micro-app-head>内,会对script标签和link标签的内容进行加载并执行

  5. <micro-app-head><micro-app-body>插入到<micro-app>标签内

  6. <micro-app>内提供了js沙箱方法(v1.0以前跟qiankun沙箱一样),<micro-app-head>挂载到<micro-app>后,内部会逐一对<micro-app-head>内的script标签的js绑定作用域,实现js隔离。

css隔离方案

默认使用正则将CSS字符串切割成最小单元,每个单元包含一段CSS信息,将所有的信息整理生成CSSTree,遍历CSSTree的每个规则,添加前缀实现样式隔离。
c00f65a107112195f3b8d79399d08a35.png

js隔离方案

micro-app有两种方式实现js隔离,默认是跟qiankun一样采用proxy沙箱的方式隔离, 在v1.0发布后支持了基于原生iframe的隔离方式。

Micro App实战

1. 主应用入口文件
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Router from './router';
import microApp from '@micro-zoe/micro-app'microApp.start()ReactDOM.render(<React.StrictMode><Router /></React.StrictMode>,document.getElementById('root')
);
调用子应用
export function MyPage () {return (<div><h1>子应用👇</h1>// name:应用名称, url:应用地址<micro-app name='my-app' url='http://localhost:3000/'></micro-app></div>)
}
2.预览

1056226cb4d79ded77d7870097f999de.png

框架优缺点

优点
  • 接入简单,组件式引入子应用

  • 团队持续更新维护

  • js隔离、css隔离、路由同步

  • 支持子应用保活, 需要开启keep-alive模式

  • 支持fiber模式,提升主应用的渲染性能。

缺点
  • 1.0之前不支持vite,1.0之后支持了

  • 默认css隔离方式,主应用的样式还是会污染到子应用。

  • 子应用和主应用必须相同的路由模式,要么同时hash模式,要么同时history模式

  • 依赖于CustomElements和Proxy两个较新的API。Proxy暂时没有做兼容,所以对于不支持Proxy的浏览器无法运行micro-app。

- END -

关于奇舞团

奇舞团是 360 集团最大的大前端团队,代表集团参与 W3C 和 ECMA 会员(TC39)工作。奇舞团非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。

d7b18df77438cdc7236b50d386d1a984.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/304507.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第十一届能源与环境研究国际会议-可再生能源走向脱碳化(ICEER 2024)即将召开!

能源和环境是当今世界至关重要的研究和教育领域&#xff0c;持续的气候危机和对可持续发展战略的迫切需求&#xff0c;需要从能源科学到地球工程等广泛领域的变革性工程解决方案和创新。ICEER 2024为来自学术界&#xff0c;研究中心和全球工业界的工程师&#xff0c;研究人员和…

AD转换(模数转换)

一、AD的基本概念 AD转换是将时间连续和幅值连续的模拟量转换为时间离散、幅值也离散的数字量。使输出的数字量与输入的模拟量成正比。 AD转换的过程有四个阶段&#xff0c;即采样、保持、量化和编码。 采样是将连续时间信号变成离散时间信号的过程。经过采样&#xff0c;时间…

生成式AI对UiPath来说是机遇还是挑战?

企业争相通过技术革新来领跑市场&#xff0c;机器人流程自动化&#xff08;RPA&#xff09;技术更是将企业的效率和成本控制推向了新的高度。但当人工智能&#xff08;AI&#xff09;的最新进展——生成式AI登上舞台时&#xff0c;它不仅带来了变革的可能&#xff0c;还提出了一…

Java编程题 | 打印杨辉三角

大家可以关注一下专栏&#xff0c;方便大家需要的时候直接查找&#xff0c;专栏将持续更新~ 题目描述 打印出杨辉三角形&#xff08;要求打印出10行如下图&#xff09; 解题思路 初始化变量&#xff1a;设置一个变量表示行号&#xff0c;从1开始。循环打印杨辉三角形…

计算机组成原理(IO,输入输出)

1、“821.2016T1(1)”&#xff0c;表示821真题&#xff0c;2016年的题&#xff0c;T1是 选择题/填空题/大题 的第一题&#xff0c;其他类似标记也是相通 2、个人小白总结自用&#xff0c;不一定适用于其他人&#xff0c;请自行甄别 3、有任何疑问&#xff0c;欢迎私信探讨&…

centos7部署zabbix6.4.9

文章目录 [toc]一、环境准备1&#xff09;部署lnmp2&#xff09;修改配置文件3&#xff09;安装数据库 二、部署zabbix1&#xff09;下载zabbix2&#xff09;安装zabbix服务端3&#xff09;修改配置4&#xff09;开机启动5&#xff09;安装客户端 三、登录配置1&#xff09;访问…

数据结构--KMP算法

数据结构–KMP算法 首先我在这里提出以下问题&#xff0c;一会一起进行探讨 1.什么是最长公共前后缀 2. KMP算法怎么实现对匹配原理 3. 最长公共前后缀怎么求解 KMP算法可以用来解决什么问题&#xff1f; 答&#xff1a;在字符串中匹配子串&#xff0c;也称为模式匹配 分析…

mac电脑安装软件报错:无法检查更新,请检查你的互联网连接

1、点菜单栏搜索图标&#xff0c;输入&#xff1a;终端 &#xff0c;找到后&#xff0c;点击打开 2、输入以下命令&#xff1a;&#xff08;复制粘贴进去&#xff09;回车安装 /usr/sbin/softwareupdate --install-rosetta --agree-to-license 3、提示【Install of Rosetta …

电商技术揭秘十八:电商平台的云计算与大数据应用小结

电商技术揭秘相关系列文章 电商技术揭秘一&#xff1a;电商架构设计与核心技术 电商技术揭秘二&#xff1a;电商平台推荐系统的实现与优化 电商技术揭秘三&#xff1a;电商平台的支付与结算系统 电商技术揭秘四&#xff1a;电商平台的物流管理系统 电商技术揭秘五&#xf…

spring boot 集成 flyway依赖 做数据库迁移,让部署没烦恼

flyway 是一个敏捷工具&#xff0c;用于数据库的移植。采用 Java 开发&#xff0c;支持所有兼容 JDBC 的数据库。 主要用于在你的应用版本不断升级的同时&#xff0c;升级你的数据库结构和里面的数据。 还是直接上代码 第一步&#xff1a; <!-- Flyway 数据库迁移 依赖 他…

MySQL排序原理与优化方法(9/16)

order by排序优化 MySQL排序策略 内存临时表 or 磁盘临时表&#xff1f; **内存临时表排序&#xff1a;**在MySQL中&#xff0c;使用InnoDB引擎执行排序操作时&#xff0c;当处理的数据量较小&#xff0c;可以在内存中完成排序时&#xff0c;MySQL会优先使用内存进行排序操作…

知名的开源大模型及其特点

目前&#xff0c;开源的大模型领域涌现出了许多具有不同特点和优势的模型。这些开源大模型不仅推动了AI技术的发展&#xff0c;也为研究者和开发者提供了丰富的资源和工具&#xff0c;促进了AI应用的创新和多样化。以下是一些知名的开源大模型及其特点。北京木奇移动技术有限公…

Python测试框架之pytest详解

前言 Python测试框架之前一直用的是unittestHTMLTestRunner&#xff0c;听到有人说pytest很好用&#xff0c;所以这段时间就看了看pytest文档&#xff0c;在这里做个记录。 官方文档介绍&#xff1a; Pytest is a framework that makes building simple and scalable tests e…

2024年ERP软件上中下游结构分析及细分行业研究

环洋咨询Global Info Research的ERP软件市场调研报告提供ERP软件市场的基本概况&#xff0c;包括定义&#xff0c;分类&#xff0c;应用和产业链结构&#xff0c;同时还讨论发展政策和计划以及制造流程和成本结构&#xff0c;分析ERP软件市场的发展现状与未来市场趋势&#xff…

String类(1)

❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&a…

三年Android开发经验面试经历分享

最近&#xff0c;参加了多家公司的面试&#xff0c;下面是我所经历的一些面试问题及自己的回答思路。 一、京东面试 一面&#xff1a; 项目内容&#xff1a;主要讲述了在实习期间参与的项目&#xff0c;以及在项目中负责的工作和取得的成果。MVP模式&#xff1a;解释了MVP模…

CSS实现三栏自适应布局(两边固定,中间自适应)

绝对定位的元素会脱离文档流&#xff0c;它们是相对于包含块&#xff08;通常是最近的具有相对定位、绝对定位或固定定位属性的父元素&#xff09;进行定位的。当你把一个绝对定位的元素的高度设置为100%时&#xff0c;它会相对于其包含块的高度来确定自己的高度。如果包含块是…

政安晨:【深度学习神经网络基础】(六)—— 前馈神经网络

目录 简述 前馈神经网络结构 计算输出 初始化权重 径向基函数神经网络 径向基函数 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: 政安晨的机器学习笔记 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎…

Centos7源码方式安装Elasticsearch 7.10.2单机版

版本选择参考&#xff1a;Elasticsearch如何选择版本-CSDN博客 下载 任选一种方式下载 官网7.10.2版本下载地址&#xff1a; https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.10.2-linux-x86_64.tar.gz 网盘下载链接 链接&#xff1a;https://pan…

OpenGL Assimp 加载3D模型介绍

OpenGL Assimp 加载3D模型介绍 Assimp对应模型结构体解说 所有的模型、场景数据都包含在scene对象中&#xff0c;如所有的材质和Mesh。同样&#xff0c;场景的根节点引用也包含在这个scene对象中 场景的Root node&#xff08;根节点&#xff09;可能也会包含很多子节点和一个…