【微前端】qiankun 使用指南:从零搭建微前端项目
技术栈:qiankun + React + Webpack
一、什么是微前端
1.1 概念
微前端就是把一个超大型应用 拆分成多个独立开发、独立部署、独立运行的小应用,再通过主应用把它们整合起来。
1.2 核心价值
| 特性 | 说明 |
|---|---|
| 技术栈无关 | 主框架不限制接入应用的技术栈,微应用具备完全自主权 |
| 独立开发、独立部署 | 微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新 |
| 增量升级 | 微前端是一种非常好的实施渐进式重构的手段和策略 |
| 独立运行时 | 每个微应用之间状态隔离,运行时状态不共享 |
1.3 为什么不用 iframe?
iframe 不是也能实现吗?能,但问题不少:
- url 不同步:刷新后 iframe url 状态丢失
- UI 不同步:DOM 结构不共享,弹窗只能在 iframe 内部显示
- 全局上下文完全隔离:内存变量不共享,Cookie、LocalStorage 不互通
- 慢:每次子应用进入都是一次浏览器上下文重建、资源重新加载
二、qiankun 简介
2.1 什么是 qiankun
qiankun 是蚂蚁基于single-spa封装的微前端库。
2.2 特性
- 任意前端框架,React/Vue/Angular/JQuery 都能接入
- 样式隔离,微应用之间样式不会互相污染
- JS 沙箱,全局变量/事件隔离,不怕冲突
- 资源预加载,浏览器空闲时提前加载,切换更快
三、快速上手
3.1 项目结构
bash
micro-frontend/
├── main-app/ # 主应用(基座)
└── micro-react/ # 微应用 - React
3.2 主应用配置
安装 qiankun
bash
yarn add qiankun
注册微应用
在主应用入口文件中配置:
javascript
// main-app/src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
import { registerMicroApps, start } from 'qiankun'
import './index.css'
// 注册微应用
registerMicroApps([
{
name: 'm-app',
entry: 'http://localhost:8001',
container: '#subapp-container',
activeRule: '/',
}
])
// 启动 qiankun
start()
// 渲染容器
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<div id="subapp-container"></div>
</React.StrictMode>
)
主应用 HTML 结构
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>微前端主应用</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
3.3 微应用配置
修改入口文件
javascript
// micro-react/src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'
let root: ReactDOM.Root | null = null
// 渲染函数
function render(props: any = {}) {
const { container } = props
const rootElement = container
? container.querySelector('#root')
: document.getElementById('root')
if (rootElement) {
root = ReactDOM.createRoot(rootElement)
root.render(
<React.StrictMode>
<App {...props} />
</React.StrictMode>
)
}
}
// 判断是否在 qiankun 环境中运行
if (!(window as any).__POWERED_BY_QIANKUN__) {
render()
}
// 导出 qiankun 生命周期函数
export async function bootstrap() {
console.log('[React子应用] bootstrap')
}
export async function mount(props: any) {
render(props)
}
export async function unmount() {
root?.unmount()
root = null
}
修改 webpack 配置
javascript
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const packageName = require('./package.json').name;
module.exports = {
entry: './src/index.tsx',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.[contenthash].js',
clean: true,
// qiankun 子应用必须配置
library: `${packageName}-[name]`,
libraryTarget: 'umd',
chunkLoadingGlobal: `webpackJsonp_${packageName}`,
globalObject: 'window',
},
resolve: {
extensions: ['.tsx', '.ts', '.js', '.jsx'],
},
module: {
rules: [
{
test: /\.(ts|tsx|js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
['@babel/preset-react', { runtime: 'automatic' }],
'@babel/preset-typescript',
],
},
},
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
title: 'React 子应用',
}),
],
devServer: {
port: 8001,
hot: true,
historyApiFallback: true,
headers: {
'Access-Control-Allow-Origin': '*',
},
},
};
跨域问题
注意:微应用必须开启 CORS,不然主应用加载不了
javascript
// webpack devServer 配置
devServer: {
headers: {
'Access-Control-Allow-Origin': '*',
}
}