为什么使用微前端?
微前端实践好文
一大型项目的基石是由多个不同的技术栈子项目组成;其主要优点:
- 技术栈无关: 主应用不限制子应用接入的技术栈,每个应用的技术栈选型可以配合业务情景选择。
- 独立开发、独立部署:既可以组合运行,也可以单独运行。
- 环境隔离:应用之间 JavaScript、CSS 隔离避免互相影响
- 消息通信:统一的通信方式,降低使用通信的成本
- 依赖复用:解决依赖、公共逻辑需要重复维护的问题
微前端的主流框架是qiankun.js; qiankun的使用类似于iframe标签的
微前端的简单使用(qiankun)
qiankun主要是由一个主应用(mainApp)及多个的微应用(microApp)组合而来;为了更好的说明不同框架应用,打算使用的vue3 做基座 , vue3和react为子应用来展示项目;
mainApp — 基座的搭建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
pnpm i qiankun -S
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([ { name: 'reactApp', entry: '//localhost:3000', container: '#container', activeRule: '/app-react', }, { name: 'vueApp', entry: '//localhost:8080', container: '#container', activeRule: '/app-vue', } ]);
start();
|
microApp- subvue的搭建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| pnpm add vite-plugin-qiankun
if (window.__POWERED_BY_QIANKUN__) { __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; }
import { renderWithQiankun, qiankunWindow} from "vite-plugin-qiankun/es/helper";
if (!qiankunWindow.__POWERED_BY_QIANKUN__) { render({}); }
function render(props: any) { const { container } = props; const router = createRouter({ history: createWebHistory( qiankunWindow.__POWERED_BY_QIANKUN__ ? "/demo1" : "/" ), routes, }); root = createApp(app); root.use(router); const dom = container? container.querySelector("#app2"): document.getElementById("app2"); root.mount(dom); }
|
micro-react的搭建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| if (window.__POWERED_BY_QIANKUN__) { __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; }
import './public-path'; import React from 'react'; import ReactDOM from 'react-dom'; import App from './App';
function render(props) { const { container } = props; ReactDOM.render(<App />, container ? container.querySelector('#root') : document.querySelector('#root')); }
if (!window.__POWERED_BY_QIANKUN__) { render({}); }
export async function bootstrap() { console.log('[react16] react app bootstraped'); }
export async function mount(props) { console.log('[react16] props from main framework', props); render(props); }
export async function unmount(props) { const { container } = props; ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root')); }
const { name } = require('./package.json'); console.log(name)
module.exports = { webpack: function override(config, env) { config.entry = config.entry.filter( (e) => !e.includes('webpackHotDevClient') );
config.output.library = `react`; config.output.libraryTarget = 'umd'; config.output.jsonpFunction = `webpackJsonp_${name}`; return config; }, devServer: (configFunction) => { return function (proxy, allowedHost) { const config = configFunction(proxy, allowedHost); config.open = false; config.hot = false; config.headers = { 'Access-Control-Allow-Origin': '*', }; return config; }; }, };
|
子应用的搭建说明:
- 引入public-path.js 文件 ,修改运行时的publicPath;
- 微应用建议使用
history 模式的路由,需要设置路由 base,值和它的 activeRule 是一样的。
- 在入口文件最顶部引入
public-path.js,修改并导出三个生命周期函数。
- 修改
webpack 打包,允许开发环境跨域和 umd 打包

- qiankun 会用 原生fetch方法,请求微应用的 entry 获取微应用资源,然后通过 response.text 把获取内容转为字符串。
- 将 HTML 字符串传入 processTpl 函数,进行 HTML 模板解析,通过正则匹配 HTML 中对应的 javaScript(内联、外联)、css(内联、外联)、代码注释、entry、ignore 收集并替换,去除
html/head/body 等标签,其他资源保持原样
- 将收集的
styles 外链URL对象通过 fetch 获取 css,并将 css 内容以 <style> 的方式替换到原来 link标签的位置
- 收集 script 外链对象,对于异步执行的 JavaScript 资源会打上
async 标识 ,会使用 requestIdleCallback 方法延迟执行。
- 接下来会创建一个匿名自执行函数包裹住获取到的 js 字符串,最后通过 eval 去创建一个执行上下文执行 js 代码,通过传入 proxy 改变 window 指向,完成 JavaScript 沙箱隔离。源码位置 。
- 由于 qiankun 是自执行函数执行微应用的 JavaScript,因此在加载后的微应用中是看不到 JavaScript 资源引用的,只有一个资源被执行替换的标识。
- 当一切准备就绪的时候,执行微应用的 JavaScript 代码,渲染出微应用