微前端的入门实践

TJF.
为什么使用微前端?

微前端实践好文

一大型项目的基石是由多个不同的技术栈子项目组成;其主要优点:

  • 技术栈无关: 主应用不限制子应用接入的技术栈,每个应用的技术栈选型可以配合业务情景选择。
  • 独立开发、独立部署:既可以组合运行,也可以单独运行。
  • 环境隔离:应用之间 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
//  使用vue3+ vite 搭建基座 
// 引入qiankun
pnpm i qiankun -S
// 在项目的main.ts 文件注册子应用
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',
}
]);

// 启动 qiankun
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
// 使用vite 搭建的vue3 子应用 由于qiankun不支持vite 所以引入vite-plugin-qiankun做适配
pnpm add vite-plugin-qiankun

// 在mian.ts统计目录下添加public-path.js 添加此文件是由于项目在运行时和构建时的路径指向不一样;用于修改运行时的 publicPath
if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
//__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}


// 在src/main中添加以下内容
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
// 引入 public-path.js
if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_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'));
}

// 增加 config-overrides.js 文件

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 your customised Webpack Development Server config.
return config;
};
},
};

子应用的搭建说明:

  • 引入public-path.js 文件 ,修改运行时的publicPath;
  • 微应用建议使用 history 模式的路由,需要设置路由 base,值和它的 activeRule 是一样的。
  • 在入口文件最顶部引入 public-path.js,修改并导出三个生命周期函数。
  • 修改 webpack 打包,允许开发环境跨域和 umd 打包

1663724165618

  • 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 代码,渲染出微应用
  • 标题: 微前端的入门实践
  • 作者: TJF.
  • 创建于 : 2022-09-20 00:00:00
  • 更新于 : 2024-03-04 08:40:28
  • 链接: https://github.com/taowind/2022/09/20/微前端的入门探索/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
此页目录
微前端的入门实践