From b14e3d11867b24513d42dbb52348dcd800f4574c Mon Sep 17 00:00:00 2001
From: cirry <812852553@qq.com>
Date: Sun, 21 Jun 2026 10:57:32 +0800
Subject: [PATCH] =?UTF-8?q?=E6=8E=A5=E5=85=A5vue3=E5=AD=90=E5=BA=94?=
=?UTF-8?q?=E7=94=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitignore | 1 +
docs/micro-app多子应用接入实战.md | 1247 +++++++++++++++++++++++++++++
src/App.vue | 1 +
src/config/subApps.ts | 9 +
src/router/index.ts | 11 +
src/views/About.vue | 11 +
6 files changed, 1280 insertions(+)
create mode 100644 docs/micro-app多子应用接入实战.md
create mode 100644 src/views/About.vue
diff --git a/.gitignore b/.gitignore
index a4d699a..132ab41 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
node_modules
dist
+.idea
*.local
.DS_Store
diff --git a/docs/micro-app多子应用接入实战.md b/docs/micro-app多子应用接入实战.md
new file mode 100644
index 0000000..6647700
--- /dev/null
+++ b/docs/micro-app多子应用接入实战.md
@@ -0,0 +1,1247 @@
+# micro-app 多子应用接入实战面试题
+
+> 基于在已有主应用中接入第二个 Vue 3 + Vite 子应用(端口 3001)的完整实战经验整理。
+
+---
+
+## 一、多子应用注册与路由
+
+### Q1:如何在已有 micro-app 主应用中新增一个子应用?需要改哪些文件?
+
+**答:**
+只需要改 **3 个文件**,遵循「配置 → 路由 → 导航」的顺序:
+
+**1. `src/config/subApps.ts` — 添加子应用配置**
+
+```ts
+export const subApps: SubAppConfig[] = [
+ {
+ name: 'vue2-app',
+ url: 'http://localhost:5173/',
+ baseroute: '/child-app',
+ iframe: true,
+ keepAlive: true,
+ routerMode: 'native'
+ },
+ // ✅ 新增
+ {
+ name: 'vue3-app',
+ url: 'http://localhost:3001/',
+ baseroute: '/vue3-app',
+ iframe: true,
+ keepAlive: true,
+ routerMode: 'native'
+ }
+]
+```
+
+**2. `src/router/index.ts` — 添加通配路由**
+
+```ts
+const routes = [
+ // ... 其他路由
+ {
+ path: '/vue3-app/:page*', // ✅ 新增:通配符匹配子应用所有内部路由
+ name: 'vue3App',
+ component: () => import('@/views/ChildApp.vue')
+ }
+]
+```
+
+**3. `src/App.vue` — 导航栏添加入口**
+
+```html
+Vue3 子应用
+```
+
+**不需要改的文件:**
+- `ChildApp.vue` — 容器组件是通用的,根据 URL 自动匹配配置
+- `main.ts` — `microApp.start()` 是框架级初始化,不绑定具体子应用
+- `vite.config.ts` — `isCustomElement` 配置对所有 `` 标签生效
+
+---
+
+### Q2:为什么要设计成"3 个文件改动"的模式?能否进一步减少?
+
+**答:**
+这是 micro-app 架构设计中最简洁的扩展模式:
+
+| 改动 | 为什么必须 |
+|------|-----------|
+| `subApps.ts` | 子应用的元信息(name、url、baseroute)必须有地方存储 |
+| `router/index.ts` | Vue Router 需要知道哪些 URL 前缀交给子应用处理 |
+| `App.vue` | 用户需要入口点击进入子应用 |
+
+**能否合并?**
+
+可以。如果子应用数量很多,可以优化为**自动注册**模式:
+
+```ts
+// router/index.ts — 自动为所有子应用生成路由
+import { subApps } from '@/config/subApps'
+
+const subAppRoutes = subApps.map(app => ({
+ path: `${app.baseroute}/:page*`,
+ name: app.name,
+ component: () => import('@/views/ChildApp.vue')
+}))
+
+const routes = [
+ { path: '/', redirect: '/home' },
+ { path: '/home', component: () => import('@/views/Home.vue') },
+ ...subAppRoutes // 自动展开
+]
+```
+
+导航栏同理,用 `v-for` 遍历 `subApps` 自动生成。这样新增子应用只需要改 `subApps.ts` 一个文件。
+
+---
+
+### Q3:`ChildApp.vue` 组件是如何做到"一个组件服务所有子应用"的?
+
+**答:**
+核心原理是通过 **路由路径匹配配置**:
+
+```ts
+// ChildApp.vue 的核心逻辑
+const currentApp = computed(() => {
+ const matched = subApps.find((app) =>
+ route.path.startsWith(app.baseroute)
+ )
+ return matched || subApps[0]
+})
+```
+
+```html
+
+/>
+```
+
+**工作流程:**
+1. 用户访问 `/vue3-app/dashboard`
+2. Vue Router 匹配到通配路由 → 渲染 `ChildApp.vue`
+3. `ChildApp.vue` 内的 `computed` 检查 `route.path` 以哪个 `baseroute` 开头
+4. 找到 `vue3-app` 配置 → 绑定到 `` 标签
+5. micro-app 框架根据 `name` + `url` 加载对应的子应用
+
+**关键设计点:**
+- `:key="currentApp.url"` — 当切换到不同子应用时,Vue 会销毁旧的 `` 重建新的
+- `keepAlive: true` — 切换回来时不销毁,保持应用状态
+- 未匹配到任何 `baseroute` 时的兜底逻辑:`|| subApps[0]`
+
+---
+
+## 二、路由冲突与优先级
+
+### Q4:如果主应用有一个 `/about` 路由,子应用也有一个 `/about` 路由,且子应用没有设置 router base,访问时会发生什么?
+
+**答:**
+**结论:不会冲突,展示哪个取决于 URL。**
+
+| 访问路径 | 展示内容 | 原因 |
+|----------|----------|------|
+| `/about` | 主应用的 About | 主应用路由直接匹配,不经过子应用 |
+| `/vue3-app/about` | 子应用的 About | 主应用路由匹配 `/vue3-app/:page*`,交给子应用处理 |
+
+**完整流程图:**
+
+```
+浏览器输入: http://localhost:8080/about
+ │
+ ▼
+ 主应用 Vue Router 开始匹配
+ │
+ ┌───────┴───────┐
+ │ │
+ 主应用有 /about 主应用没有 /about
+ │ │
+ ▼ ▼
+ 展示主应用 About 404 / fallback
+ (子应用完全不会
+ 被加载)
+```
+
+```
+浏览器输入: http://localhost:8080/vue3-app/about
+ │
+ ▼
+ 主应用 Vue Router 匹配
+ │
+ ▼
+ 命中 /vue3-app/:page* 通配路由
+ │
+ ▼
+ 渲染 ChildApp.vue → 加载
+ │
+ ▼
+ micro-app 剥离 baseroute(/vue3-app)
+ 传给子应用内部路由: /about
+ │
+ ▼
+ 子应用 Vue Router 匹配 /about
+ │
+ ▼
+ 展示子应用 About 页面
+```
+
+---
+
+### Q5:子应用不设置 router base 会有什么隐患?
+
+**答:**
+虽然 micro-app 的 `baseroute` 机制能拦截 pushState 并自动拼接前缀,让大部分场景"看起来正常",但仍有 **4 个隐患**:
+
+| 隐患 | 说明 | 风险等级 |
+|------|------|----------|
+| `window.location` 误用 | 代码中直接读 `location.pathname` 会得到完整路径 `/vue3-app/about`,而不是期望的 `/about` | ⭐⭐⭐ 高 |
+| 静态资源 404 | `
` 会请求主应用的 8080 端口而非子应用的 3001 端口 | ⭐⭐⭐ 高 |
+| 独立运行失败 | 不经过主应用直接访问子应用时,base 为空字符串,路由跳转可能出错 | ⭐⭐ 中 |
+| `history.go()` 异常 | 直接操作浏览器历史时不经过 micro-app 拦截,可能跳出微前端上下文 | ⭐ 低 |
+
+**最佳实践:**
+
+```ts
+// 子应用 router/index.ts
+import { createRouter, createWebHistory } from 'vue-router'
+
+const router = createRouter({
+ history: createWebHistory(
+ // 动态适配:微前端环境用 baseroute,独立运行用 '/'
+ window.__MICRO_APP_BASE_ROUTE__ || '/'
+ ),
+ routes: [
+ { path: '/about', component: () => import('@/views/About.vue') }
+ ]
+})
+```
+
+`window.__MICRO_APP_BASE_ROUTE__` 是 micro-app 注入到子应用 window 上的全局变量,值就是主应用中配置的 `baseroute`。
+
+---
+
+### Q6:micro-app 是如何实现"主应用路由和子应用路由互不干扰"的?
+
+**答:**
+通过 **URL 命名空间隔离** 机制:
+
+```
+主应用路由空间(Vue Router 管理)
+├── / → 重定向到 /home
+├── /home → Home.vue
+├── /about → About.vue (如果定义了)
+├── /child-app/:page* → 子应用 vue2-app 的领地
+└── /vue3-app/:page* → 子应用 vue3-app 的领地
+
+子应用 vue2-app 路由空间(内部 Router 管理,剥离 /child-app 前缀后)
+├── / → 首页
+├── /about → 关于页
+└── /user/:id → 用户详情
+
+子应用 vue3-app 路由空间(内部 Router 管理,剥离 /vue3-app 前缀后)
+├── / → 首页
+├── /about → 关于页
+└── /dashboard → 控制台
+```
+
+**三层隔离保证:**
+
+1. **Vue Router 层级** — 主应用的 `/about` 和通配路由 `/vue3-app/:page*` 互斥匹配,不会同时命中
+2. **baseroute 剥离** — micro-app 在将 URL 传给子应用前,自动去除 baseroute 前缀
+3. **iframe 沙箱** — Vite 子应用运行在独立 iframe 中,`window.location` 指向自己的 origin
+
+---
+
+## 三、SubAppConfig 接口设计
+
+### Q7:`SubAppConfig` 接口各字段的作用和设计考量是什么?
+
+**答:**
+
+```ts
+export interface SubAppConfig {
+ name: string // 全局唯一标识,字母开头
+ url: string // 子应用 devServer / 生产地址
+ baseroute: string // 主应用分配给子应用的 URL 命名空间
+ iframe?: boolean // 是否使用 iframe 沙箱
+ keepAlive?: boolean // 切换时是否保活
+ routerMode?: 'native' | 'native-scope'
+ disableScopecss?: boolean // 禁用样式隔离
+ disableSandbox?: boolean // 禁用 JS 沙箱
+}
+```
+
+| 字段 | 为什么可选/必填 | 设计考量 |
+|------|----------------|----------|
+| `name` | 必填 | micro-app 框架需要唯一标识来管理子应用生命周期 |
+| `url` | 必填 | 不知道地址就无法加载;生产环境应通过环境变量注入 |
+| `baseroute` | 必填 | 决定 URL 命名空间,也影响子应用的资源路径解析 |
+| `iframe` | 可选,默认 false | Vite 子应用必须设为 true(ES Module 兼容性) |
+| `keepAlive` | 可选 | 高频切换场景开启可提升体验,但占用内存 |
+| `routerMode` | 可选 | `native-scope` 提供额外的作用域隔离 |
+| `disableScopecss` | 可选 | 调试时临时关闭样式隔离,排查样式问题 |
+| `disableSandbox` | 可选 | 调试时临时关闭沙箱,确认是否是沙箱引起的 bug |
+
+---
+
+### Q8:为什么 Vite 子应用的 `iframe` 必须设为 `true`?
+
+**答:**
+见 [基础面试题 Q7](./micro-app面试题.md#q7遇到-cannot-use-import-statement-outside-a-module-报错是什么原因如何解决),核心原因:
+
+```
+Vite devServer 输出 ES Module (type="module")
+ ↓
+with 沙箱用 new Function() 执行 JS
+ ↓
+new Function() 无法解析 import/export 语法
+ ↓
+❌ SyntaxError: Cannot use import statement outside a module
+```
+
+**解决:** `iframe: true` → 浏览器原生 iframe 天然支持 `
+
+```
+
+```js
+// Webpack 打包后的 JS 格式(UMD / IIFE):
+(function(module, exports, __webpack_require__) {
+ // 所有模块代码都在一个函数闭包里
+ // 没有 import/export 语句!
+ var Vue = __webpack_require__('vue')
+ // ...
+})
+```
+
+**关键特征:** Webpack 打包产物是**普通脚本(Regular Script)**,不包含 `import`/`export` 语句。
+
+**with 沙箱的工作方式:**
+
+```
+micro-app with 沙箱执行流程:
+
+1. 获取子应用的 JS 代码(字符串)
+2. 用 with(window.proxy) 创建一个作用域链
+3. 通过 new Function(code) 执行 JS
+
+ 伪代码:
+ const sandboxCode = `
+ with (this.proxyWindow) {
+ ${子应用的JS代码} ← 普通 JS,可以执行 ✅
+ }
+ `
+ const fn = new Function('window', 'document', sandboxCode)
+ fn(proxyWindow, proxyDocument)
+```
+
+因为 Webpack 产出的 JS 是普通的 IIFE/UMD 代码,`new Function()` 完全可以执行它。Proxy 劫持了 `window.document` 等全局变量的访问,实现了沙箱隔离。
+
+---
+
+#### Vite 子应用:with 沙箱不可执行(必须用 iframe)
+
+**Vite devServer 的代码输出:**
+
+```html
+
+
+```
+
+```js
+// Vite 不对源码打包,直接输出 ES Module:
+import { createApp } from '/node_modules/.vite/deps/vue.js?v=abc123'
+import App from '/src/App.vue'
+import router from '/src/router/index.ts'
+// ↑ 全是 import/export 语句!
+```
+
+**关键特征:** Vite devServer 输出的是 **ES Module(`
+
+
+主应用的about
+
+
+
\ No newline at end of file