一:主应用
搭建react项目
npx create-react-app react-qiankun-main
安装Antd
npm install antd –save
在 index.js中引入
import { ConfigProvider } from "antd";
import zhCN from "antd/locale/zh_CN";
import "antd/dist/reset.css";
// ...
root.render(
<ConfigProvider locale={zhCN}>
<React.StrictMode>
<App />
</React.StrictMode>
</ConfigProvider>
);
安装react-router :
npm i react-router-dom
在 index.js中引入
import { BrowserRouter } from "react-router-dom";
// …
<BrowserRouter>
<ConfigProvider locale={zhCN}>
<React.StrictMode>
<App />
</React.StrictMode>
</ConfigProvider>
</BrowserRouter>
安装 qiankun :
npm i qiankun -S
在主应用中注册微应用,在 index.js中引入
// …
import { registerMicroApps, start } from "qiankun";
registerMicroApps([
{
name: "react app", // 子应用的名称,必须唯一。
entry: "//localhost:3004", // 子应用项目本地运行地址
container: "#lyContainer", // 子应用的容器(子应用嵌入到主项目id为lyContainer的地方)
activeRule: "/react-app", // 子应用激活时的路由规则(子应用路由)
},
{
name: "vue app",
entry: "//localhost:5173",
container: "#lyContainer",
activeRule: "/vue-app",
},
]);
start();
// …
注:子应用嵌入到主应用的地方,id要跟index.js下registerMicroApps里面的container设置一致
修改App.js文件,将如下代码放入App.js
import React, { useState } from "react";
import "./App.css";
import {
MenuFoldOutlined,
MenuUnfoldOutlined,
UserOutlined,
VideoCameraOutlined,
} from "@ant-design/icons";
import { Layout, Menu, Button } from "antd";
import { NavLink } from "react-router-dom";
const { Header, Sider, Content } = Layout;
function App() {
const [collapsed, setCollapsed] = useState(false);
return (
<div className="App">
<Layout className="layout-container">
<Sider trigger={null} collapsible collapsed={collapsed}>
<div className="logo">logo</div>
<Menu
theme="dark"
mode="inline"
defaultSelectedKeys={["1"]}
items={[
{
key: "1",
icon: <UserOutlined />,
label: <NavLink to="/vue-app">vue应用</NavLink>,
},
{
key: "2",
icon: <VideoCameraOutlined />,
label: <NavLink to="/react-app">react应用</NavLink>,
},
]}
/>
</Sider>
<Layout className="layout-main">
<Header className="header">
<Button
type="text"
icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
onClick={() => setCollapsed(!collapsed)}
className="collapse-btn"
/>
</Header>
<Content id="lyContainer" className="layout-content">
Content
</Content>
</Layout>
</Layout>
</div>
);
}
export default App;
修改App.css样式如下:
#root,
.App,
.layout-container {
width: 100%;
height: 100%;
}
.logo {
height: 64px;
line-height: 64px;
color: #fff;
text-align: center;
}
.layout-main {
overflow-y: auto;
}
.header {
padding: 0;
background-color: #fff;
}
.header .collapse-btn {
font-size: 16px;
width: 64px;
}
.layout-content{
padding: 24px;
}
效果如下:
二、react微应用
npx create-react-app react-qiankun-sub
cd react-qiankun-sub
npm start
修改index.html下的id
在src下新建public-path.js,将如下代码放进去
if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
安装react-router :
npm i react-router-dom
设置 history 模式路由的 base,并在index.js中导入public-path.js,修改index.js中代码如下:
import { BrowserRouter } from "react-router-dom";
import "./public-path";
let root = null;
function render(props) {
const { container } = props;
root =
root ||
ReactDOM.createRoot(
container
? container.querySelector("#subRoot")
: document.getElementById("subRoot")
);
root.render(
<BrowserRouter
basename={window.__POWERED_BY_QIANKUN__ ? "/react-app" : "/"}
>
<React.StrictMode>
<App />
</React.StrictMode>
</BrowserRouter>
);
}
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);
}
// https://github.com/kobeyk/micro-app-react-template/blob/main/config-overrides.js
export async function unmount(props) {
// const { container } = props;
root.unmount();
root = null;
}
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
修改webpack配置,安装craco:
npm i @craco/craco
根目录新增 craco.config.js,将如下代码放进去:
const { name } = require("./package");
module.exports = {
webpack: {
configure: (webpackConfig) => {
webpackConfig.output.library = `${name}-[name]`;
webpackConfig.output.libraryTarget = "umd";
webpackConfig.output.chunkLoadingGlobal = `webpackJsonp_${name}`;
return webpackConfig;
},
},
devServer: (devServerConfig) => {
devServerConfig.historyApiFallback = true;
devServerConfig.open = false;
devServerConfig.hot = false;
devServerConfig.watchFiles = [];
devServerConfig.headers = {
"Access-Control-Allow-Origin": "*",
};
return devServerConfig;
},
};
修改package.json里面启动的命令,因为安装了craco
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test",
"eject": "react-scripts eject"
},
三、vue3微应用
npm init vue@latest
cd vue-qiankun
npm install
npm run format
npm run dev
安装vite-plugin-qiankun:
npm i vite-plugin-qiankun
https://www.npmjs.com/package/vite-plugin-qiankun
在vite.config.js加入如下配置
import qiankun from 'vite-plugin-qiankun'
// https://vitejs.dev/config/
export default defineConfig({
// 生产环境需要指定运行域名作为base
// base: 'http://xxx.com/'
plugins: [
vue(),
qiankun('vue-app', {
// 'vue-app' 是子应用名,与主应用注册时保持一致
useDevMode: true // 如果是在主应用中加载子应用vite,必须打开这个,否则vite加载不成功, 单独运行没影响
})
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server: {
port: '5173',
cors: true,
origin: 'http://localhost:5173', // 子应用引入到主应用之后,子应用中的图片在主应用下加载不出来、找不到,需要将origin设置成子应用本地运行地址
}
})
在main.js中修改代码,如下:
import './assets/main.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import routes from './router'
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper'
import { createRouter, createWebHistory } from 'vue-router'
let router = null
let instance = null
let history = null
function render(props = {}) {
const { container } = props
history = createWebHistory(qiankunWindow.__POWERED_BY_QIANKUN__ ? '/vue-app' : '/')
router = createRouter({
history,
routes
})
instance = createApp(App)
instance.use(router)
instance.use(createPinia())
instance.mount(container ? container.querySelector('#app') : '#app')
if (qiankunWindow.__POWERED_BY_QIANKUN__) {
console.log('我正在作为子应用运行')
}
}
// some code
renderWithQiankun({
bootstrap() {
console.log('bootstrap')
},
mount(props) {
console.log('viteapp mount')
render(props)
// console.log(instance.config.globalProperties.$route,333);
},
unmount() {
console.log('vite被卸载了')
instance.unmount()
instance._container.innerHTML = ''
history.destroy() // 不卸载 router 会导致其他应用路由失败
router = null
instance = null
}
})
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
render()
}
效果如下:
问题:
- 应用间的样式隔离:
在最新的 qiankun 版本中,你也可以尝试通过配置{ sandbox : { experimentalStyleIsolation: true } }
的方式开启运行时的 scoped css 功能,从而解决应用间的样式隔离问题
在主应用的index.js下的start中加入sandbox配置 ,代码如下:
start({ sandbox : { experimentalStyleIsolation: true }});
- 主应用中切换到子应用对应的页面,子应用空白,控制台也不报错
元素检查看主应用中是否有子应用的元素,如果有,那可能是样式原因导致的