qiankun + React + Next.js 微服务搭建实战记录
本文基于《qiankun-react项目实战》中的思路,从 0 搭建一个:
- 主应用(纯 HTML + qiankun,端口 7000)
- React 微应用(基于 Vite + React 18 + react-router-dom 6,端口 5173)
并完整记录从初始化项目、接入 qiankun,到启动、联调和验证的全部过程。
一、整体架构设计
- 主应用:负责导航和微应用的注册、加载、卸载
- 使用
qiankun的registerMicroApps、startAPI - 使用简单的
http-server静态服务 - 提供一个容器 DOM:
<main id="container"></main>
- 使用
- React 微应用:业务实现
- 使用 React 18 + React Router v6
- 使用 Vite 作为构建工具
- 通过
vite-plugin-qiankun适配为 qiankun 微应用 - 路由
basename与微应用activeRule一致:/app-react
- Next 微应用:业务实现
- 使用 Next.js 13
- 通过
basePath: '/app-next'与主应用activeRule保持一致 - 在
window上导出bootstrap/mount/unmount生命周期,通过 JS Entry 或 HTML Entry 接入
二、主应用搭建过程
1. 创建主应用目录
在仓库根目录下创建主应用文件夹:
- 目录:
main-app/ - 目的:单独存放主应用的静态资源和脚本
2. 初始化主应用依赖
在 main-app 目录下准备 package.json:
json
{
"name": "main-app",
"private": true,
"version": "0.1.0",
"type": "module",
"scripts": {
"start": "http-server -p 7000 -c-1"
},
"dependencies": {
"qiankun": "^2.14.0"
},
"devDependencies": {
"http-server": "^14.1.1"
}
}
说明:
- 使用
http-server在 7000 端口提供静态服务 - 使用最新的 qiankun 2.x 版本
-c-1关闭缓存,便于开发调试
3. 编写主应用 HTML
文件:main-app/index.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>主应用 - qiankun</title>
<style>
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, 'Segoe UI Emoji'; margin: 0; }
header { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; background: #222; color: #fff; }
nav a, nav button { margin-right: 12px; color: #fff; background: transparent; border: 1px solid #fff; padding: 6px 10px; cursor: pointer; }
#container { padding: 16px; min-height: 60vh; }
#loading { padding: 16px; color: #666; }
</style>
</head>
<body>
<header>
<div>主应用</div>
<nav>
<button id="btn-home">主应用首页</button>
<button id="btn-react">进入 React 微应用</button>
<button id="btn-next">进入 Next 微应用</button>
</nav>
</header>
<div id="loading"></div>
<main id="container"></main>
<script type="module" src="./src/index.js"></script>
</body>
</html>
关键点:
#container:qiankun 微应用挂载容器- 两个按钮:分别切换到主应用首页和 React 微应用
4. 注册 React 微应用并启动 qiankun
文件:main-app/src/index.js:
javascript
import { registerMicroApps, start, initGlobalState } from 'qiankun';
const loadingEl = document.querySelector('#loading');
function setLoading(loading) {
loadingEl.textContent = loading ? '加载中...' : '';
}
const actions = initGlobalState({
theme: 'light',
user: { name: 'demo' },
});
actions.onGlobalStateChange((state) => {
console.log('global state changed', state);
});
registerMicroApps(
[
{
name: 'reactApp',
entry: 'http://localhost:5173/app-react/',
container: '#container',
activeRule: '/app-react',
props: {
token: 'demo-token',
onGlobalStateChange: actions.onGlobalStateChange,
setGlobalState: actions.setGlobalState,
},
},
{
name: 'nextApp',
entry: { scripts: ['http://localhost:3002/app-next/qiankun-lifecycle.js'] },
container: '#container',
activeRule: '/app-next',
props: {
token: 'next-token',
onGlobalStateChange: actions.onGlobalStateChange,
setGlobalState: actions.setGlobalState,
},
},
],
{
beforeLoad: [
app => {
setLoading(true);
console.log('before load', app.name);
},
],
afterMount: [app => {
setLoading(false);
console.log('after mount', app.name);
}],
afterUnmount: [app => console.log('after unmount', app.name)],
}
);
start({
prefetch: 'all',
sandbox: { experimentalStyleIsolation: true },
singular: true,
});
document.querySelector('#btn-home').addEventListener('click', () => {
history.pushState(null, '', '/');
});
document.querySelector('#btn-react').addEventListener('click', () => {
history.pushState(null, '', '/app-react/');
});
document.querySelector('#btn-next').addEventListener('click', () => {
history.pushState(null, '', '/app-next/');
});
要点:
entry指向微应用开发服务器地址:http://localhost:5173activeRule为/app-react,后续微应用的路由basename也使用同样的前缀- 通过生命周期钩子显示加载状态、打印日志,方便调试
- 使用
history.pushState切换 URL,触发 qiankun 的路由匹配 - 已注册 Next 微应用,使用 JS Entry 加载生命周期脚本,导航按钮
#btn-next进入/app-next/
至此,主应用部分搭建完成。
三、React 微应用搭建过程
本次实现选择 Vite + React 18 的形态,并结合 vite-plugin-qiankun 接入 qiankun。
1. 创建微应用目录
在仓库根目录下创建:
- 目录:
react-app/
用于承载 React 微应用的所有代码。
2. 初始化 React 微应用依赖
文件:react-app/package.json:
json
{
"name": "react-app",
"private": true,
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview --port 5173"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.22.0"
},
"devDependencies": {
"vite": "^5.0.0",
"@vitejs/plugin-react": "^4.2.0",
"vite-plugin-qiankun": "^1.0.15"
}
}
选择说明:
- 使用 React 18,方便与当前文档中的 React 18 示例对齐
- 使用 React Router v6,配合
Routes和RouteAPI - Vite 5 +
@vitejs/plugin-react提供开发体验 vite-plugin-qiankun将 Vite 项目改造为 qiankun 微应用
3. 配置 Vite 适配 qiankun
文件:react-app/vite.config.js:
javascript
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import qiankun from 'vite-plugin-qiankun';
export default defineConfig({
base: '/app-react/',
plugins: [
react(),
qiankun('reactApp', { useDevMode: true }),
],
server: {
port: 5173,
headers: {
'Access-Control-Allow-Origin': '*',
},
},
});
关键配置:
base: '/app-react/':确保构建产物中的静态资源路径与微应用部署前缀一致qiankun('reactApp', { useDevMode: true }):reactApp对应主应用中注册时的nameuseDevMode: true允许在开发环境直接通过 Vite dev server 提供微应用
server.headers:允许主应用跨域加载资源(开发阶段使用*即可)
4. 编写微应用入口 HTML
文件:react-app/index.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>React 微应用</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
与普通 Vite + React 项目基本一致,唯一需要注意的是:
- 根节点 id 为
root,在微应用生命周期中需要根据 qiankun 提供的container精确找到这个节点
5. 编写 React 入口和生命周期
文件:react-app/src/main.jsx:
javascript
import React from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper';
import App from './App.jsx';
let root = null;
function render(props = {}) {
const container = props.container || document;
const dom = container.querySelector('#root');
root = createRoot(dom);
root.render(
<BrowserRouter basename={qiankunWindow.__POWERED_BY_QIANKUN__ ? '/app-react' : '/'}>
<Routes>
<Route path="/" element={<App />} />
</Routes>
</BrowserRouter>
);
}
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
render();
}
renderWithQiankun({
bootstrap() {},
mount(props) {
render(props);
},
unmount() {
root?.unmount();
root = null;
},
});
对应关系:
qiankunWindow.__POWERED_BY_QIANKUN__:判断当前是否运行在 qiankun 容器内- 子应用独立运行(本地调试):
false,正常以/为根路由 - 被主应用加载:
true,以/app-react为basename
- 子应用独立运行(本地调试):
renderWithQiankun:包装 bootstrap/mount/unmount 生命周期mount(props):由 qiankun 在激活微应用时调用,将props.container作为根节点查找范围unmount:调用root.unmount()完整卸载 React 应用,释放资源
6. 编写简单的 React 页面
文件:react-app/src/App.jsx:
javascript
import React from 'react';
export default function App() {
return (
<div style={{ padding: 16 }}>
<h1>React 微应用</h1>
<p>这是通过 qiankun 接入的 React 微应用。</p>
</div>
);
}
此处先保持页面简单清晰,后续可以继续扩展路由和业务组件。
四、启动与联调步骤
1. 安装依赖
在项目根目录下分别安装主应用和微应用依赖(命令示例):
bash
cd main-app
npm install
cd ../react-app
npm install
2. 启动 React 微应用
进入 react-app 目录:
bash
npm run dev
Vite 默认会在 http://localhost:5173 启动开发服务器,与主应用中配置的 entry 一致。
此时可以直接访问 http://localhost:5173 验证子应用独立运行是否正常。
3. 启动主应用
进入 main-app 目录:
bash
npm run start
http-server 会在 http://localhost:7000 提供静态服务。
在浏览器访问:
- 打开
http://localhost:7000 - 点击"进入 React 微应用"按钮,地址栏变为
http://localhost:7000/app-react #container中会渲染 React 微应用的内容
4. 验证路由与卸载
- 在微应用页面中刷新浏览器,确认路由
basename配置正确 - 点击浏览器"后退",确认微应用卸载,主应用首页恢复正常
可以通过控制台日志进一步确认:
before load reactAppafter mount reactApp- 切换到其他路由后,看到
after unmount reactApp
五、与文档内容的对应关系
1. 主应用部分
文档中的主应用示例:
- 使用
registerMicroApps注册微应用数组 - 调用
start()启动 qiankun - 可以配置
prefetch、sandbox、singular等参数
本实战中:
- 完整使用了
registerMicroApps和start,并结合生命周期钩子实现加载状态展示 - 在
start中启用了prefetch: 'all'、sandbox.strictStyleIsolation、singular: true,与文档中的最佳实践保持一致
2. React 微应用部分
文档给出的 React 微应用方案主要基于:
- CRA + webpack +
public-path.js - ReactDOM.render / ReactDOM.unmountComponentAtNode
- React Router v5 的
BrowserRouter+basename
本实战做了如下调整:
- 使用 Vite 替代 CRA + webpack,简化配置
- 使用 React 18 的
createRoot/root.unmount - 使用 React Router v6 的
Routes+RouteAPI - 通过
vite-plugin-qiankun替代手写public-path.js和生命周期导出
同时保持了关键的一致性:
- 路由前缀
basename与activeRule保持一致:/app-react - 独立运行与被主应用加载两种模式都可兼容
六、踩坑与经验
-
路由前缀必须一致
- 主应用注册微应用时
activeRule: '/app-react' - 微应用中
BrowserRouter的basename必须使用同样的前缀
- 主应用注册微应用时
-
开发环境跨域
- 主应用和微应用端口不同(7000 与 5173)
- 主应用通过
entry加载微应用的 HTML 和静态资源,需要设置 CORS 头 - 本实战在 Vite 的
server.headers中设置了Access-Control-Allow-Origin: '*'
-
React 18 卸载方式
- React 18 推荐使用
createRoot/root.unmount,不再使用ReactDOM.unmountComponentAtNode - 保证在
unmount生命周期中调用root.unmount(),避免内存泄漏
- React 18 推荐使用
-
独立运行与微应用模式兼容
- 通过判断
qiankunWindow.__POWERED_BY_QIANKUN__分支渲染 - 在独立模式下直接
render(),在微应用模式下由renderWithQiankun接管
- 通过判断
-
严格样式隔离的权衡
strictStyleIsolation会使用 Shadow DOM 包裹微应用根节点- 可以有效隔离样式,但某些依赖全局样式的组件库可能需要额外适配
七、总结
通过本次实战,我们完成了:
- 从 0 搭建一个 qiankun 主应用
- 接入一个 React 18 + React Router v6 + Vite 的微应用
- 实现微应用独立运行与被主应用加载两种模式
- 应用文档中提到的关键概念:
activeRule、basename、生命周期钩子、沙箱配置等
在此基础上,可以继续扩展:
- 增加更多微应用(例如 Vue、Angular)
- 接入全局状态管理,实现主应用与微应用之间的实时通信
- 构建更完善的监控与埋点系统,追踪每个微应用的加载与运行情况
这篇记录可作为在团队内推广 qiankun + React + Next.js 微前端实践的参考示例。
八、排错指南:React 微应用生命周期导出失败
- 现象
- 进入 React 微应用时报错:
You need to export lifecycle functions in reactApp entry - 控制台同时出现
[import-html-entry]: error occurs while executing normal script <script type="module">import { injectIntoGlobalHook } from "/app-react/@react-refresh" ...,页面停留在"加载中"
- 进入 React 微应用时报错:
- 根因
- Vite 开发模式会注入 React Fast Refresh 的模块脚本(type="module")
- qiankun 的 HTML entry 将脚本当作普通脚本执行,无法处理 ES Module 的 import,导致生命周期注册代码未执行,从而判定未导出 lifecycles
- 修复步骤
- 关闭 Fast Refresh 与 HMR,以避免注入模块脚本
- 修改 [react-app/vite.config.js](file:///d:/otherProject/qiankun-app-example/react-app/vite.config.js):
react({ fastRefresh: false }),server.hmr: false
- 修改 [react-app/vite.config.js](file:///d:/otherProject/qiankun-app-example/react-app/vite.config.js):
- 改用 UMD/JS Entry 方式加载子应用,避免 HTML 解析与模块注入问题
- 在微应用添加 UMD 构建配置:见 [react-app/vite.config.js](file:///d:/otherProject/qiankun-app-example/react-app/vite.config.js) 的
build.lib - 构建并启动静态资源服务(含 CORS):
- 执行
npm run build生成dist/index.umd.cjs - 执行
npm run serve启动 5174 端口(已开启--cors)
- 执行
- 主应用注册改为 JS Entry:
-
main-app/src/index.js\](file:///d:/otherProject/qiankun-app-example/main-app/src/index.js#L1-L77) 使用 `entry: { scripts: ['http://localhost:5174/index.umd.cjs'] }`
-
- 打开
http://localhost:7000/app-react,正常加载子应用;控制台不再出现@react-refresh相关错误
- 在微应用添加 UMD 构建配置:见 [react-app/vite.config.js](file:///d:/otherProject/qiankun-app-example/react-app/vite.config.js) 的
- 关闭 Fast Refresh 与 HMR,以避免注入模块脚本
九、UMD/JS Entry 与 HTML Entry 的差异说明
- HTML Entry
- 入口类型:子应用的 HTML 地址(包含脚本、样式)
- 执行方式:qiankun 拉取 HTML,解析并按顺序执行脚本与样式
- 优点:与多数前端构建产物匹配,结构直观
- 注意:开发模式下若注入 ES Module(如 React Fast Refresh),会因
type="module"导致执行失败;需关闭相关注入或改用 JS Entry
- JS Entry(UMD)
- 入口类型:脚本数组(通常为 UMD/可直接执行的 JS)
- 执行方式:qiankun 直接加载并执行脚本,期待脚本在全局导出 lifecycles
- 优点:避免 HTML 解析与模块注入差异,开发联调更稳
- 注意:需开启 CORS;并保证打包产物以 UMD 形式导出并在
window下可访问(vitebuild.lib的name对应全局变量名)
- 推荐实践
- 开发环境:优先使用 JS Entry(UMD)+ 关闭 Fast Refresh/HMR,保证稳定加载
- 生产环境:可使用 HTML Entry(构建后的静态 HTML),避免开发注入脚本;确保资源路径与
activeRule/basename一致 - 始终保证路由前缀一致:
activeRule与子应用路由basename使用同一前缀
十、检查清单(集成前后自测)
-
React 微应用
- vite
base与主应用activeRule前缀一致(/app-react) - 若使用开发模式:
fastRefresh: false,server.hmr: false - UMD 构建配置正确(
build.lib),产出index.umd.cjs - 静态服务开启 CORS(
--cors)
- vite
-
主应用
- React 微应用使用 JS Entry 引入 UMD 脚本
-
main-app/src/index.js\](file:///d:/otherProject/qiankun-app-example/main-app/src/index.js#L1-L77) 的 `entry.scripts` 指向 `http://localhost:5174/index.umd.cjs`
-
- 生命周期钩子有统一 loader/错误兜底展示
- React 微应用使用 JS Entry 引入 UMD 脚本
-
Next 微应用
basePath: '/app-next'与activeRule: '/app-next'一致-
next-app/next.config.js\](file:///d:/otherProject/qiankun-app-example/next-app/next.config.js)
-
next-app/public/qiankun-lifecycle.js\](file:///d:/otherProject/qiankun-app-example/next-app/public/qiankun-lifecycle.js),并在 \[pages/_document.js\](file:///d:/otherProject/qiankun-app-example/next-app/pages/_document.js) 注入
-
-
现象
- 进入 Next 微应用时,控制台报错:
application 'nextApp' died in status LOADING_SOURCE_CODE: Failed to fetch - 指向的资源通常是页面中注入的生命周期脚本或其他静态资源
- 进入 Next 微应用时,控制台报错:
-
根因
- qiankun 的
import-html-entry拉取子应用 HTML 与脚本时会使用fetch,并可能携带凭据(credentials: include) - 当请求携带凭据时,CORS 不允许
Access-Control-Allow-Origin: *,必须返回具体来源,并且需要允许凭据
- qiankun 的
-
修复
- 在 Next 中设置明确的跨域响应头(针对主应用的来源):
- 示例:在 [next-app/next.config.js](file:///d:/otherProject/qiankun-app-example/next-app/next.config.js) 中配置
Access-Control-Allow-Origin: http://localhost:7000Access-Control-Allow-Credentials: trueAccess-Control-Allow-Methods: GET,OPTIONSAccess-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept
- 示例:在 [next-app/next.config.js](file:///d:/otherProject/qiankun-app-example/next-app/next.config.js) 中配置
- 保证入口 URL 使用尾斜杠(目录语义),避免相对路径解析错误
- 主应用注册 Next:
entry: { scripts: ['http://localhost:3002/app-next/qiankun-lifecycle.js'] },见 [main-app/src/index.js](file:///d:/otherProject/qiankun-app-example/main-app/src/index.js#L1-L77)
- 主应用注册 Next:
- 保证静态资源路径与
basePath前缀一致- 例如生命周期脚本:在 [next-app/pages/_document.js](file:///d:/otherProject/qiankun-app-example/next-app/pages/_document.js) 注入
/app-next/qiankun-lifecycle.js
- 例如生命周期脚本:在 [next-app/pages/_document.js](file:///d:/otherProject/qiankun-app-example/next-app/pages/_document.js) 注入
- 在 Next 中设置明确的跨域响应头(针对主应用的来源):
-
验证
- 重启 Next dev 后,在主应用访问
/app-next,资源加载成功,错误消失 - 网络面板中可见
Access-Control-Allow-Origin返回为主应用地址,且响应包含Access-Control-Allow-Credentials: true
- 重启 Next dev 后,在主应用访问
常见问题
qiankun.js?v=562334ee:4903
Uncaught TypeError: application 'nextApp' died in status LOADING_SOURCE_CODE: application 'nextApp' died in status LOADING_SOURCE_CODE: application 'nextApp' died in status LOADING_SOURCE_CODE: application 'nextApp' died in status LOADING_SOURCE_CODE: application 'nextApp' died in status LOADING_SOURCE_CODE: http://localhost:3001/qiankun-lifecycle.js Failed to fetch
Uncaught TypeError: application 'nextApp' died in status LOADING_SOURCE_CODE: application 'nextApp' died in status LOADING_SOURCE_CODE: Failed to fetch
Uncaught TypeError: application 'nextApp' died in status LOADING_SOURCE_CODE: application 'nextApp' died in status LOADING_SOURCE_CODE: Failed to fetch
十二、Next.js 微应用集成(本仓库实作)
本仓库已新增 Next 微应用目录:next-app/,并完成与主应用的集成,点击主应用导航中的"进入 Next 微应用"即可进入。
1. Next 应用结构与关键文件
- 目录:
next-app/ - 关键文件:
- 包管理与脚本:[package.json](file:///d:/otherProject/qiankun-app-example/next-app/package.json)
- 基础配置(路由前缀与 CORS):[next.config.js](file:///d:/otherProject/qiankun-app-example/next-app/next.config.js)
- 文档注入生命周期脚本:[pages/_document.js](file:///d:/otherProject/qiankun-app-example/next-app/pages/_document.js)
- 微应用首页:[pages/index.js](file:///d:/otherProject/qiankun-app-example/next-app/pages/index.js)
- 导出 qiankun 生命周期脚本:[public/qiankun-lifecycle.js](file:///d:/otherProject/qiankun-app-example/next-app/public/qiankun-lifecycle.js)
2. 配置要点
- 路由前缀:
basePath: '/app-next'与主应用activeRule: '/app-next'保持一致
见 [next-app/next.config.js](file:///d:/otherProject/qiankun-app-example/next-app/next.config.js) - 跨域:为主应用来源
http://localhost:7000设置允许跨域与凭据
见 [next-app/next.config.js](file:///d:/otherProject/qiankun-app-example/next-app/next.config.js) 的headers() - 生命周期导出:在窗口对象上导出
bootstrap/mount/unmount
见 [next-app/public/qiankun-lifecycle.js](file:///d:/otherProject/qiankun-app-example/next-app/public/qiankun-lifecycle.js) - 注入脚本:在文档中通过
<script src="/app-next/qiankun-lifecycle.js">注入
见 [next-app/pages/_document.js](file:///d:/otherProject/qiankun-app-example/next-app/pages/_document.js)
3. 主应用注册与导航
主应用已注册 Next 微应用,入口为带尾斜杠的目录语义:
- 注册与按钮事件:
-
main-app/src/index.js\](file:///d:/otherProject/qiankun-app-example/main-app/src/index.js#L30-L72)
-
- 入口地址:
entry: { scripts: ['http://localhost:3002/app-next/qiankun-lifecycle.js'] } - 点击"进入 Next 微应用"按钮后,地址栏变为
http://localhost:7000/app-next/
4. 启动与联调
- 安装依赖并启动 Next:
- 进入
next-app/:npm install && npm run dev(默认端口 3002)
- 进入
- 启动主应用:
- 进入
main-app/:npm install && npm run start(默认端口 7000)
- 进入
- 浏览器访问:
- 打开
http://localhost:7000,点击"进入 Next 微应用" #container容器中渲染 Next 微应用页面
- 打开
5. 卸载与返回
- 点击浏览器"后退"返回主应用首页,微应用触发
unmount,清空#__next内容 - 可在控制台观察生命周期触发日志与 CORS 响应头
十三、开发模式稳定加载方案(JS Entry + 独立生命周期服务)
为避免 Next 开发模式下 HTML Entry/模块脚本导致的加载不稳定,采用以下稳定方案:
核心思路
- 改为 JS Entry:主应用仅加载生命周期脚本,避免 HTML 解析差异
- 独立生命周期静态服务:明确返回主应用来源与允许凭据的 CORS 响应头
- 生命周期函数返回 Promise,满足 single-spa/qiankun 的异步要求
改动点(本仓库)
- 主应用 entry 指向独立生命周期服务
-
main-app/src/index.js\](file:///d:/otherProject/qiankun-app-example/main-app/src/index.js#L30-L34)
-
- 独立生命周期静态服务(端口 3004)
-
next-app/scripts/serve-lifecycle.js\](file:///d:/otherProject/qiankun-app-example/next-app/scripts/serve-lifecycle.js)
- 地址:
http://localhost:3004/app-next/qiankun-lifecycle.js
-
- 生命周期脚本返回 Promise
-
next-app/public/qiankun-lifecycle.js\](file:///d:/otherProject/qiankun-app-example/next-app/public/qiankun-lifecycle.js)
-
- 可选:Next 中间件统一返还 CORS 头(开发时)
-
next-app/middleware.js\](file:///d:/otherProject/qiankun-app-example/next-app/middleware.js)
-
- 启动 Next dev:
npm run dev(端口 3002) - 启动生命周期服务:
npm run serve:lifecycle(端口 3004) - 启动主应用:
npm run start(端口 7000) - 在主应用页面点击"进入 Next 微应用",应正常显示内容;来回切换 React/Next 页面均稳定
说明
- 开发联调推荐 JS Entry + 独立脚本服务以确保稳定;生产可回到构建后的 HTML Entry,并确保
basePath与activeRule一致且服务端返回明确 CORS。
十四、为何 Next 使用 JS Entry(而非 HTML Entry)
-
背景
- 主应用在注册 Next 微应用时使用 JS Entry:
- 参考 [main-app/src/index.js:L31-L32](file:///d:/otherProject/qiankun-app-example/main-app/src/index.js#L31-L32)
entry: { scripts: ['http://localhost:3002/app-next/qiankun-lifecycle.js'] }
- 非 HTML Entry:未直接使用
entry: 'http://localhost:3002/app-next/'
- 主应用在注册 Next 微应用时使用 JS Entry:
-
原因
- 生命周期确定性:JS Entry 的脚本会在
window上明确导出bootstrap/mount/unmount,qiankun 能稳定调用;HTML Entry 依赖页面脚本自行导出,Next 默认并不会这么做,容易出现"未导出生命周期"的报错 - 开发模式更稳:Next dev 会注入 HMR、Overlay 等模块脚本,HTML Entry 的解析与执行更易受这些差异影响;JS Entry 不解析 HTML,仅加载生命周期脚本,避免干扰
- CORS 与凭据:HTML Entry 通过
import-html-entry拉取页面与脚本,通常包含credentials;若服务端未针对主应用来源返回精确的 CORS 响应头,易报Failed to fetch;JS Entry 只需拉取一个脚本,跨域头配置更简单、可控 - 路径语义:HTML Entry 要求入口使用尾斜杠(目录语义)且与
basePath一致,否则相对资源解析错位;JS Entry 只需一个明确的脚本 URL,避免路径陷阱
- 生命周期确定性:JS Entry 的脚本会在
-
何时可使用 HTML Entry
- 满足以下前提即可改回 HTML Entry:
- Next 配置
basePath: '/app-next'且在 [next-app/next.config.js](file:///d:/otherProject/qiankun-app-example/next-app/next.config.js) 中为主应用来源http://localhost:7000返回明确的 CORS 头(含Access-Control-Allow-Credentials: true) - 在文档中注入生命周期脚本:见 [pages/_document.js](file:///d:/otherProject/qiankun-app-example/next-app/pages/_document.js) 使用
<script src="/app-next/qiankun-lifecycle.js" defer></script> - 主应用入口地址使用尾斜杠目录语义:
entry: 'http://localhost:3002/app-next/'
- Next 配置
- 满足以下前提即可改回 HTML Entry:
-
建议
- 开发阶段优先 JS Entry,保证加载稳定与生命周期确定性
- 生产构建后可使用 HTML Entry,但务必确保上述 CORS、路径与生命周期导出条件满足
效果图
