📚 核心概念:SPA 路由模式
1. 传统 SPA 应用的路由流程
css
css
用户访问 → Nginx → HTML → 前端路由
示例:独立运行的 React 应用
bash
nginx
# 主应用(独立 SPA)
location / {
root /data/apps/frontend/main;
try_files $uri $uri/ /index.html; # ✅ 需要 try_files
}
为什么需要 try_files?
ruby
bash
# 用户直接访问深层路由
https://example.com/users/profile
# Nginx 处理流程:
1. 查找文件:/data/apps/frontend/main/users/profile → ❌ 不存在
2. 查找目录:/data/apps/frontend/main/users/profile/ → ❌ 不存在
3. Fallback:返回 /index.html → ✅ 让前端路由接管
# 浏览器接收到 index.html
# React Router 解析 URL:/users/profile
# 渲染对应组件
没有 try_files 会发生什么?
shell
bash
# 用户访问:https://example.com/users/profile
# Nginx:文件不存在 → 404 错误 ❌
# 前端路由永远不会执行
🎯 Qiankun 微前端架构的不同之处
2. Qiankun 的路由职责划分
scss
scss
主应用 微应用
↓ ↓
前端路由 静态资源
控制页面 (JS/CSS/图片)
关键区别:
- ✅ 主应用:负责所有路由控制(包括微应用路由)
- ✅ 微应用:只提供静态资源(JS Bundle)
- ❌ 微应用:不直接处理 URL 路由
3. 实际请求流程对比
场景 A:主应用路由(需要 try_files)
bash
bash
# 1️⃣ 用户直接访问主应用路由
https://example.com/dashboard
# 2️⃣ Nginx 处理
location / {
root /data/apps/frontend/main;
try_files $uri $uri/ /index.html; # ✅ 必须
}
# 3️⃣ 流程
用户访问 /dashboard
↓
Nginx: /dashboard 文件不存在
↓
try_files fallback → 返回 /index.html
↓
主应用 React Router 解析 /dashboard
↓
渲染 Dashboard 页面
场景 B:微应用路由(不需要 try_files)
bash
bash
# 1️⃣ 用户访问微应用路由
https://example.com/keyboard-management
# 2️⃣ 主应用处理(不是 Nginx!)
主应用 index.html 加载
↓
主应用 React Router 解析 /keyboard-management
↓
Qiankun 决定加载微应用
↓
请求微应用资源:
- /keyboard/keyboard-management.js ← ✅ 直接请求 JS 文件
- /keyboard/index.html ← ❌ 不会请求
↓
微应用 JS 执行并挂载
关键点:
- 🔴 用户永远不会直接访问微应用的 HTML 路由
- 🟢 所有路由都由主应用控制
- 🟢 微应用只被当作 JS 库加载
4. 具体代码示例
主应用路由配置(React Router)
javascript
javascript
// 主应用 App.tsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Routes>
{/* ✅ 主应用路由 */}
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
{/* ✅ 微应用路由(由主应用控制) */}
<Route path="/keyboard-management/*" element={
<MicroAppContainer name="keyboard-management" />
} />
<Route path="/mouse-management/*" element={
<MicroAppContainer name="mouse-management" />
} />
</Routes>
</BrowserRouter>
);
}
// ✅ 微应用容器组件
function MicroAppContainer({ name }) {
useEffect(() => {
// Qiankun 加载微应用
loadMicroApp({
name,
entry: `/keyboard/`, // ← 只请求静态资源
container: '#micro-app-container',
});
}, []);
return <div id="micro-app-container" />;
}
用户访问流程
bash
bash
# 用户输入 URL:https://example.com/keyboard-management
# ↓
# 1️⃣ 浏览器请求
GET https://example.com/keyboard-management
↓
# 2️⃣ Nginx 处理(主应用配置)
location / {
root /data/apps/frontend/main;
try_files $uri $uri/ /index.html; # ✅ fallback 到主应用
}
# 返回:/data/apps/frontend/main/index.html
↓
# 3️⃣ 主应用加载
<script src="/main.js"></script> # 主应用 JS
↓
# 4️⃣ React Router 解析
URL: /keyboard-management
Route 匹配: <Route path="/keyboard-management/*" ... />
↓
# 5️⃣ Qiankun 加载微应用
loadMicroApp({ entry: '/keyboard/' })
↓
# 6️⃣ 请求微应用资源(AJAX 请求)
GET /keyboard/index.html # Qiankun 解析入口
GET /keyboard/keyboard-management.js # ← 真正的 JS 文件
GET /keyboard/xxx.css
↓
# 7️⃣ Nginx 处理微应用资源请求
location /keyboard/ {
alias /data/apps/frontend/keyboard/;
# ❌ 不需要 try_files
# 因为这些都是真实的文件请求,不是路由
}
# 返回:实际的 JS/CSS 文件内容
🔍 详细对比:主应用 vs 微应用
主应用请求模式
表格
| 请求类型 | URL 示例 | Nginx 处理 | 结果 |
|---|---|---|---|
| 首页 | / |
文件存在 → 返回 index.html |
✅ |
| 路由 | /dashboard |
文件不存在 → try_files → index.html |
✅ |
| 路由 | /users/123 |
文件不存在 → try_files → index.html |
✅ |
| 静态资源 | /main.js |
文件存在 → 返回 JS | ✅ |
没有 try_files:
bash
bash
用户访问 /dashboard
↓
Nginx: 404 Not Found ❌
前端路由永远不会执行
微应用请求模式
表格
| 请求类型 | URL 示例 | 请求发起者 | Nginx 处理 |
|---|---|---|---|
| 入口 HTML | /keyboard/ |
Qiankun | 返回 index.html ✅ |
| JS Bundle | /keyboard/keyboard-management.js |
Qiankun | 返回 JS 文件 ✅ |
| CSS | /keyboard/style.css |
Qiankun | 返回 CSS 文件 ✅ |
| 图片 | /keyboard/logo.png |
微应用 | 返回图片 ✅ |
| ❌ 路由 | /keyboard/page1 |
不会发生 | - |
关键:
- 🔴 用户不会直接访问
/keyboard/page1 - 🟢 只有主应用控制路由,然后加载微应用资源
- 🟢 所有请求都是实际的文件路径
💡 实际验证
情况 1:如果微应用配置了 try_files
bash
nginx
# ❌ 错误配置
location /keyboard/ {
alias /data/apps/frontend/keyboard/;
try_files $uri $uri/ /keyboard/index.html; # 会出问题
}
会发生什么?
bash
bash
# Qiankun 请求:/keyboard/keyboard-management.js
# ↓
# Nginx 处理:
1. 尝试文件:/data/apps/frontend/keyboard/keyboard-management.js
# 如果文件存在 → ✅ 返回 JS(正常)
# 如果文件不存在(路径错误) → ❌ 继续
2. 尝试目录:/data/apps/frontend/keyboard/keyboard-management.js/
# 不存在 → 继续
3. Fallback:返回 /keyboard/index.html # ❌ 问题在这里!
# 浏览器期望收到 JS,却收到 HTML
# 结果:
Uncaught SyntaxError: Unexpected token '<'
# 因为浏览器把 HTML 当 JavaScript 解析了
情况 2:不配置 try_files(正确)
bash
nginx
# ✅ 正确配置
location /keyboard/ {
alias /data/apps/frontend/keyboard/;
# 不配置 try_files
}
会发生什么?
bash
bash
# Qiankun 请求:/keyboard/keyboard-management.js
# ↓
# Nginx 处理:
1. 直接查找文件:/data/apps/frontend/keyboard/keyboard-management.js
# 如果存在 → ✅ 返回 JS
# 如果不存在 → ✅ 返回 404
# 结果:
# 文件存在 → 微应用正常加载 ✅
# 文件不存在 → 明确的 404 错误,易于排查 ✅
🎯 特殊情况:微应用内部路由
问题:微应用内部有子路由怎么办?
xml
javascript
// 微应用内部路由
<BrowserRouter basename="/keyboard-management">
<Routes>
<Route path="/" element={<List />} />
<Route path="/create" element={<Create />} /> // 子路由
<Route path="/edit/:id" element={<Edit />} /> // 子路由
</Routes>
</BrowserRouter>
答案:依然不需要 Nginx 的 try_files!
原因:
bash
bash
# 1️⃣ 用户访问:https://example.com/keyboard-management/create
# ↓
# 2️⃣ 主应用处理
主应用路由匹配: /keyboard-management/*
↓
Qiankun 加载微应用
↓
# 3️⃣ 微应用 JS 执行
微应用 React Router 解析: /keyboard-management/create
↓
渲染 Create 组件 ✅
# 🔴 注意:整个过程中,浏览器只请求了一次 HTML(主应用的)
# 微应用的子路由完全在前端内存中处理,不涉及 Nginx
📊 总结表格
表格
| 对比项 | 主应用 | 微应用 |
|---|---|---|
| 路由控制 | 自己控制 | 主应用控制 |
| 用户访问方式 | 直接访问 URL | 不会直接访问 |
| Nginx 职责 | 返回 HTML + 处理路由 fallback | 只返回静态资源 |
| 需要 try_files | ✅ 是 | ❌ 否 |
| 请求类型 | HTML 页面 + 静态资源 | 只有静态资源 |
| 404 情况 | Fallback 到 index.html | 真实的 404 错误 |
✅ 最佳实践总结
1. 主应用 Nginx 配置
bash
nginx
# ✅ 需要 try_files
location / {
root /data/apps/frontend/main;
try_files $uri $uri/ /index.html; # 处理 SPA 路由
}
原因:
- 用户会直接访问任意路由
- 需要 fallback 到 index.html 让前端路由接管
2. 微应用 Nginx 配置
bash
nginx
# ✅ 不需要 try_files
location /keyboard/ {
alias /data/apps/frontend/keyboard/;
# 直接返回文件,不做 fallback
}
原因:
- 只提供静态资源服务
- 所有请求都是真实文件路径
- 路由由主应用和微应用前端代码控制
3. 记忆口诀
主应用:用户入口,需要路由 fallback → try_files ✅
微应用:资源仓库,直接文件访问 → 不需要 try_files ❌
🔧 调试技巧
如何验证是否需要 try_files?
markdown
bash
# 问自己 3 个问题:
1. 用户会直接在浏览器输入这个路径吗?
- 主应用:会(/dashboard, /users/123)→ 需要
- 微应用:不会(由 Qiankun 加载)→ 不需要
2. 这个路径是真实文件还是前端路由?
- 真实文件(/app.js, /logo.png)→ 不需要
- 前端路由(/dashboard)→ 需要
3. 谁在控制这个路由?
- 用户(直接访问)→ 需要 Nginx fallback
- 前端代码(AJAX 加载)→ 不需要 Nginx fallback
希望这个解释清楚了!核心就是:
- 🎯 微应用在 Qiankun 架构中只是"静态资源包",不是独立的 SPA 应用
- 🎯 所有路由控制权都在主应用,微应用只负责提供 JS/CSS/图片等文件
- 🎯 Nginx 对微应用只需要做"文件服务器",不需要处理路由 fallback