搭建主应用
主应用不限技术栈,只需要提供一个容器 DOM,然后注册微应用并 start 即可。
1、下载项目
// TS
npx create-react-app qk-main --template typescript
// JS
npx create-react-app qk-mai
2、安装 qiankun
yarn add qiankun 或者 npm i qiankun -S
3、基于路由配置自动注册微应用
// src/index.tsx
// 引入配置函数
import { registerMicroApps, start } from "qiankun";
//配置两个子应用
registerMicroApps([
{
// 子应用名字
name: "qk-micro-react",
// 子应用容器id 需要在父应用中创建相关元素
container: "#qk-micro-react",
// 子应用进入地址
entry: "http://localhost:3011",
// 激活子应用的路由
activeRule: "/qk-micro-react",
},
{
name: "qk-micro-vue",
container: "#qk-micro-vue",
entry: "http://localhost:3012/",
activeRule: "/qk-micro-vue",
},
]);
//启动 qiankun
start({
sandbox: {
// 样式隔离特性
experimentalStyleIsolation: true,
},
});
代码预览
4、添加子应用对应的容器 (仔细看注释 这里比较容易出错嘞)
// App.tsx
import React from "react";
import "./App.css";
import { Link, BrowserRouter } from "react-router-dom";
function App() {
return (
<div className="App">
{/* 先做一个简单的路由跳转 */}
<BrowserRouter>
<h3>
<Link to="/qk-micro-react">微前端React</Link>
</h3>
<h3>
<Link to="/qk-micro-vue">微前端Vue</Link>
</h3>
</BrowserRouter>
{/* id 要与 registerMicroApps 当中的 container 对齐 */}
{/* react子应用挂载容器 id=qk-micro-react */}
<div id="qk-micro-react" />
{/* vue子应用挂在容器 id=qk-micro-vue */}
<div id="qk-micro-vue" />
</div>
);
}
export default App;
页面预览
搭建 react 子应用
第一步是增加 public-path.js 文件,用于修改运行时的 publicPath,修改 history 模式的路由,但是要区分独立运行和在 qiankun 中运行两种环境,在入口文件导出微应用的生命周期函数,最后修改 webpack 打包,允许开发环境跨域和 umd 打包
1、下载项目
// TS
npx create-react-app qk-main --template typescript
// JS
npx create-react-app qk-main
2、在 src 目录下面新增 public-path.ts
如果不添加 可能会出现静态资源无法加载的问题,子应用在微服务中打开 它默认使用了父应用的前缀,导致子应用无法加载自身的静态资源
if (window.__POWERED_BY_QIANKUN__) {
// @ts-ignore
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
export {};
3、导出生命周期函数,修改路由基础路径
如果对这几个生命周期函数不理解的 可以查阅 single-spa docs
// 入口文件 index.tsx
//引入新增的 public-path 文件
import "./public-path";
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { BrowserRouter } from "react-router-dom";
let root: ReactDOM.Root;
function createRoot(props: Record<string, any>) {
// container中包含了qiankun创建的dom,它会插入一个带有id为root的dom
const { container } = props;
root = ReactDOM.createRoot(
container
? container.querySelector("#root")
: document.querySelector("#root")
);
}
// 独立运行,直接调用 createRoot函数 render
if (!window.__POWERED_BY_QIANKUN__) {
createRoot({});
// @ts-ignore
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
}
// lifecycle => 初始化
export async function bootstrap(props: Record<string, any>) {
console.log(props);
}
// lifecycle => 挂载
export async function mount(props: Record<string, any>) {
createRoot(props);
//qiankun环境中渲染
root.render(
<BrowserRouter
// 对两种不同的环境分别给出不同的基础路径
basename={window.__POWERED_BY_QIANKUN__ ? "/qk-micro-react" : "/"}
>
<App />
</BrowserRouter>
);
}
// lifecycle => 卸载
export async function unmount(_props: Record<string, any>) {
root.unmount();
}
reportWebVitals();
4、修改 webpack 配置
需要借助 react-app-rewired 或 @rescripts/cli 插件来完成对 webpack 配置的修改,我会分两种方法来描述
方法一:
安装插件 @rescripts/cli,
yarn add @rescripts/cli 或 npm i -D @rescripts/cli
根目录新增 .rescriptsrc.js:
const { name } = require("./package");
module.exports = {
webpack: (config) => {
config.output.library = `${name}-[name]`;
config.output.libraryTarget = "umd";
// config.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;
},
};
修改 package.json 文件
"scripts": {
"start": "rescripts start",
"build": "rescripts build",
"test": "rescripts test",
"eject": "rescripts eject"
},
方法二:
安装插件 react-app-rewired
yarn add react-app-rewired 或 npm i -D react-app-rewired
根目录下新增 config-overrides.js 文件
const { name } = require("./package");
module.exports = {
webpack: (config) => {
config.output.library = `${name}-[name]`;
config.output.libraryTarget = "umd";
// config.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;
},
};
修改 package.json 文件
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-app-rewired eject"
},
启动项目时指定 3011 端口 或者 根目录添加.ENV 文件指定 3011 启动端口
port=3011
项目独立运行时
项目在微服务中打开时
如果微服务中无法打开 仔细查阅前面的注册配置是否有问题
搭建 vue3 + vite 子应用
目前 qiankun 还没有兼容 vite,还需借助第三方插件 vite-plugin-qiankun 来实现
1、下载项目
自选配置,尽量带路由,方便后续验证
npm init vue@latest
2、vite.config.ts 中添加插件,修改路由配置
import { fileURLToPath, URL } from "node:url";
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import qiankun from "vite-plugin-qiankun";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
// 添加qiankun插件
qiankun("qk-micro-vue", {
useDevMode: true,
}),
],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
server: {
port: 3012,
// 如果出现图片资源无法加载 请加上跨域
origin: "http://localhost:3012",
cors: true,
},
});
修改路由配置,分两种情况.
1、独立运行. 2、微服务中运行.
3. main.ts 文件添加生命周期
在 vite-plugin-qiankun 插件中导出 initQianKun 函数,在函数中定义生命周期函数。
再分别对独立运行和微前端中运行做出不同的渲染
指定 3012 端口启动项目
单独运行:
微前端环境中运行:
路由切换:
关于 experimentalStyleIsolation
官方文档中表明 experimentalStyleIsolation 只是实验性的样式隔离特性,神说要有光的这篇文章给出的例子易懂,建议阅读
当 experimentalStyleIsolation 被设置为 true 时,qiankun 会改写子应用所添加的样式为所有样式规则增加一个特殊的选择器规则来限定其影响范围,因此改写后的代码会表达类似为如下结构:
// 假设应用名是 react16
.app-main {
font-size: 14px;
}
div[data-qiankun-react16] .app-main {
font-size: 14px;
}
之前两个子应用的选择器规则都发生了改变,均加上了 data-qiankun = 'yourself app name'
总结
当应用在微前端中运行的时候,它的 window 会变成一个经过 Proxy 代理的对象
应用通过判断 window.__POWERED_BY_QIANKUN 的布尔值来确定运行的环境
这篇文章主要是给对 qiankun docs 阅读不明白的 jym 出的一个小 demo,特别是 vite 这一块,网上完整的文章不算太多,官网又不兼容 vite,但 vite 又是趋势。这种场景挺尴尬的,希望今年 qiankun 能对 vite 也做出点兼容性上的改变。