From 850f7543b2e0b41f352cc4109c83aa8fd144ceed Mon Sep 17 00:00:00 2001
From: cirry <812852553@qq.com>
Date: Sat, 27 Jun 2026 00:27:46 +0800
Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=9D=A2=E8=AF=95=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.reasonix/desktop-topic-created-at.json | 3 +-
.reasonix/desktop-topic-title-sources.json | 4 +-
.reasonix/desktop-topic-titles.json | 4 +-
docs/vite.md | 1890 +++++++++++++++-----
package-lock.json | 5 -
qodana.yaml | 46 +
src/main.js | 1 +
7 files changed, 1500 insertions(+), 453 deletions(-)
create mode 100644 qodana.yaml
diff --git a/.reasonix/desktop-topic-created-at.json b/.reasonix/desktop-topic-created-at.json
index baa3934..77066b9 100644
--- a/.reasonix/desktop-topic-created-at.json
+++ b/.reasonix/desktop-topic-created-at.json
@@ -1,3 +1,4 @@
{
- "topic_20260626-084849_ae13b749f70ea556": 1782463729575
+ "topic_20260626-084849_ae13b749f70ea556": 1782463729575,
+ "topic_20260626-155701_2112a87ef28bf822": 1782489421463
}
\ No newline at end of file
diff --git a/.reasonix/desktop-topic-title-sources.json b/.reasonix/desktop-topic-title-sources.json
index 0d49143..5aa67ab 100644
--- a/.reasonix/desktop-topic-title-sources.json
+++ b/.reasonix/desktop-topic-title-sources.json
@@ -1,5 +1,7 @@
{
"topic_20260626-084849_ae13b749f70ea556": "auto",
"topic_20260626-085627_e3928116af40710e": "auto",
- "topic_20260626-093001_b040128cfc0f086c": "auto"
+ "topic_20260626-093001_b040128cfc0f086c": "auto",
+ "topic_20260626-155701_2112a87ef28bf822": "auto",
+ "topic_20260626-160318_753694613866863b": "auto"
}
\ No newline at end of file
diff --git a/.reasonix/desktop-topic-titles.json b/.reasonix/desktop-topic-titles.json
index 82b8ed8..27da4fb 100644
--- a/.reasonix/desktop-topic-titles.json
+++ b/.reasonix/desktop-topic-titles.json
@@ -1,5 +1,7 @@
{
"topic_20260626-084849_ae13b749f70ea556": "这个文件中的plugin调用的函数h…",
"topic_20260626-085627_e3928116af40710e": "我写这个项目主要是用做面试用的,所以…",
- "topic_20260626-093001_b040128cfc0f086c": "帮我在plugins/plugin…"
+ "topic_20260626-093001_b040128cfc0f086c": "帮我在plugins/plugin…",
+ "topic_20260626-155701_2112a87ef28bf822": "第五个问题关于钩子生命周期的回答写的…",
+ "topic_20260626-160318_753694613866863b": "帮我添加10个前端工程化中常问的题目…"
}
\ No newline at end of file
diff --git a/docs/vite.md b/docs/vite.md
index 1fff1a2..a99370e 100644
--- a/docs/vite.md
+++ b/docs/vite.md
@@ -1,617 +1,1617 @@
# Vite 面试题(含详细解答)
-> 基于本项目的 Vue 3 + Vite + 自定义插件实战经验整理。
+> 基于本项目的 Vue 3 + Vite + 自定义插件实战经验整理。
----
+---
## 1. Vite 和 Webpack 在项目开发中有哪些区别?
-这是一道高频面试题,关键不是背概念,而是从**开发体验、构建机制、配置复杂度、生态兼容性**四个维度说清楚。
+这是一道高频面试题,关键不是背概念,而是从**开发体验、构建机制、配置复杂度、生态兼容性**四个维度说清楚。
### 1.1 核心差异:开发阶段的模块处理方式
-**Webpack** 是一个 **Bundle-based** 的构建工具。开发时它会从入口文件出发,递归解析所有依赖,把所有模块打包成一个(或多个)bundle,然后启动 dev server。每次代码变更,它需要重新打包受影响的模块并刷新 bundle,这就是为什么大项目中 Webpack 的 HMR 有时会明显变慢。
+**Webpack** 是一个 **Bundle-based** 的构建工具。开发时它会从入口文件出发,递归解析所有依赖,把所有模块打包成一个(或多个)bundle,然后启动 dev server。每次代码变更,它需要重新打包受影响的模块并刷新 bundle,这就是为什么大项目中 Webpack 的 HMR 有时会明显变慢。
-**Vite** 在开发阶段不走打包流程,而是利用浏览器原生支持的 **ES Module (ESM)**。它直接把 `.vue`、`.ts`、`.jsx` 等源码文件按需编译后通过 ESM 发给浏览器。浏览器通过 `
+```
+
+### 24.3 SSR 的工程化挑战
+
+| 挑战 | 说明 | 解决方案 |
+| ------------------ | ---------------------------------------------- | ------------------------------------- |
+| **服务端无 DOM** | `window`/`document` 在 Node.js 中不存在 | 用 `import.meta.client` / `process.client` 守卫 |
+| **数据同步** | 服务端获取的数据要传递给客户端,避免重复请求 | Nuxt 的 `useFetch` / Next 的 `getServerSideProps` |
+| **内存泄漏** | 服务端每个请求都创建 Vue 实例,容易泄漏 | 使用工厂函数创建实例,避免单例 |
+| **缓存策略** | 每次请求都渲染太慢 | 页面级缓存(Redis)、组件级缓存 |
+| **部署复杂度** | 需要 Node.js 服务器,而不是纯静态文件 | Serverless(Vercel/Netlify)、容器化 |
+
+### 24.4 什么时候选哪种模式
+
+```
+需要 SEO + 实时数据? → SSR(电商、新闻、社区)
+需要 SEO + 内容不常变?→ SSG(博客、文档、官网)
+不需要 SEO? → CSR(后台管理系统、工具型应用)
+混合场景? → ISR / 混合渲染(部分页面 SSR,部分 SSG)
+```
+
+---
+
+## 25. 如何做前端项目的依赖治理?
+
+### 25.1 依赖治理的三个维度
+
+```
+依赖治理
+├── 安全维度 — 漏洞修复(npm audit)
+├── 版本维度 — 升级策略 & 兼容性管理
+└── 体积维度 — 依赖瘦身 & 重复检测
+```
+
+### 25.2 安全漏洞修复
+
+```bash
+# 审计当前项目依赖
+npm audit # 查看漏洞列表
+npm audit fix # 自动修复(仅 semver 兼容的补丁版本)
+npm audit fix --force # 强制修复(可能包含 breaking change)
+
+# pnpm
+pnpm audit
+```
+
+**生产环境的漏洞处理流程:**
+
+```
+1. npm audit → 发现高危/严重漏洞
+2. 查看是否影响运行时(devDependencies 的漏洞可能无需紧急处理)
+3. 尝试 npm audit fix(不破坏项目的前提下自动升级)
+4. 如果 fix 失败 → 手动升级单个包 → 跑测试 → 部署
+5. 建立定期审计机制(CI 中跑 npm audit --audit-level=high)
+```
+
+### 25.3 版本升级策略
+
+| 策略 | 做法 | 风险 | 适用场景 |
+| ---------- | ---------------------------------------- | ---- | -------------- |
+| 激进升级 | 经常升级到最新版 | 高 | 个人项目 |
+| 保守升级 | 只在需要新功能/修复时升级 | 低 | 生产项目 |
+| 定期批处理 | 每月/每季度集中升级一次 | 中 | **推荐** |
+| 工具辅助 | Renovate / Dependabot 自动提 PR | 中 | 团队协作 |
+
+**Renovate 配置示例:**
+
+```json
+// renovate.json
+{
+ "extends": ["config:base"],
+ "schedule": ["before 8am on Monday"], // 每周一早上自动提 PR
+ "packageRules": [
+ {
+ "matchUpdateTypes": ["patch"],
+ "automerge": true // patch 自动合并
+ },
+ {
+ "matchDepTypes": ["devDependencies"],
+ "automerge": true // 开发依赖自动合并
+ }
+ ]
+}
+```
+
+### 25.4 依赖瘦身
+
+**① 找出重复/冗余依赖**
+
+```bash
+npx depcheck # 找出来安装但未使用的包
+npx npm-dedupe # 去重 npm 依赖树
+pnpm dedupe # pnpm 去重
+```
+
+**② 分析依赖体积**
+
+```bash
+# 可视化分析
+npx vite-bundle-visualizer # Vite 项目
+npx webpack-bundle-analyzer # Webpack 项目
+
+# 命令行快速查看
+npx cost-of-modules # 每个包的大小
+```
+
+**③ 替代重型依赖**
+
+| 重包 | 轻量替代 | 体积差异 |
+| ----------- | ---------------- | -------------- |
+| `moment` | `dayjs` | 72KB → 2KB |
+| `lodash` | `lodash-es` + 按需引入 | 全量 70KB → 按需 2-3KB |
+| `axios` | `ky` / `ofetch` | 13KB → 3KB |
+| `antd` | 按需引入 + tree-shaking | 视情况定 |
+
+### 25.5 CI 中的依赖质量门禁
+
+```yaml
+# GitHub Actions 示例
+- name: Dependency Check
+ run: |
+ npm audit --audit-level=high # 高危漏洞阻断 CI
+ npx depcheck --ignores="eslint,prettier" # 检查未使用依赖
+ npx bundlesize # 检查产物大小是否超标
+```
+
+---
+
+## 26. dev server proxy 的原理是什么?跨域问题有哪些工程化解决方案?
+
+### 26.1 为什么会有跨域问题
+
+浏览器的**同源策略(Same-Origin Policy)**:协议、域名、端口三者必须完全相同才能自由通信。开发时,Vite dev server 跑在 `localhost:5173`,后端 API 跑在 `localhost:3000`——端口不同,跨域了。
+
+```
+开发阶段 生产阶段
+localhost:5173 ──跨域──→ api.example.com 前端域名: app.example.com
+(前端) (后端) 后端域名: api.example.com
+ 不同子域名也跨域!
+```
+
+### 26.2 三种工程化方案
+
+**方案一:dev server proxy(开发环境首选)**
+
+```js
+// vite.config.js
+export default defineConfig({
+ server: {
+ proxy: {
+ '/api': {
+ target: 'http://localhost:3000',
+ changeOrigin: true,
+ rewrite: (path) => path.replace(/^\/api/, '')
+ }
}
}
})
```
-函数语法更适合实际项目:你可以按 `node_modules` 来源拆分 vendor,按业务域拆分 page chunks,或者按体积阈值动态决定是否拆分。
+**原理**:浏览器请求 `localhost:5173/api/users` → Vite dev server 收到 → 转发给 `localhost:3000/users` → 拿到响应 → 返回浏览器。整个过程对浏览器透明,浏览器只和同源的 `localhost:5173` 通信,不存在跨域。
----
+**方案二:CORS(服务端配置)**
-## 9. Vite 的 CSS 处理有什么特点?
-
-1. **原生 CSS 支持**:直接 `import './style.css'`,Vite 自动注入到页面。
-2. **CSS Modules**:文件名以 `.module.css` 结尾,自动启用 CSS Modules。
-3. **PostCSS**:项目根目录放置 `postcss.config.js` 即可,Vite 会自动应用。
-4. **CSS 代码分割**:生产构建时,每个异步 chunk 的 CSS 会被提取为独立文件,按需加载。
-5. **`@import` 内联与 rebase**:`@import` 和 `url()` 路径会自动重写,避免开发与生产路径不一致。
-6. **预处理器**:安装 `sass` 或 `less` 即可直接使用 `.scss`、`.less` 文件,无需额外配置 loader。
-
----
-
-## 10. Vite 的静态资源处理策略是怎样的?
-
-- **小资源(< 4KB)**:自动转为 base64 内联,减少 HTTP 请求。
-- **大资源**:复制到 `dist/assets/`,文件名包含 hash 用于缓存。
-- **`public` 目录**:此目录下的资源**不会被处理**,直接复制到 `dist/` 根目录,适合 `robots.txt`、`favicon.ico` 等。
-- **`new URL`**:支持通过 `new URL('./img.png', import.meta.url)` 动态引用资源。
-
----
-
-## 11. 如何调试 Vite 的自定义插件?
-
-1. **添加 `name`**:每个插件必须有唯一的 `name`,方便定位问题。
-2. **在项目中引入 vite-plugin-inspect**
-3. **使用 console.log**:在插件的各个钩子中添加日志,观察执行时机和参数。
-**启用 debug 模式**:
- ```bash
- DEBUG=vite:* vite
- ```
-5. **使用 Vite 的 `apply` 选项**:可以限制插件只在开发或生产环境生效:
- ```js
- {
- name: 'dev-only-plugin',
- apply: 'serve' // 或 'build',分别限制开发/生产
- }
- ```
-
----
-
-## 12. Vite 项目从开发到部署的完整流程是怎样的?
-
-```bash
-# 开发
-npm run dev # vite — 启动 dev server,ESM 按需编译 + HMR
-
-# 构建
-npm run build # vite build — Rollup 打包,输出到 dist/
-
-# 本地预览生产构建
-npm run preview # vite preview — 在本地启动静态服务器预览 dist/
+```js
+// 后端 Express 示例
+app.use((req, res, next) => {
+ res.header('Access-Control-Allow-Origin', 'https://my-app.com')
+ res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE')
+ res.header('Access-Control-Allow-Headers', 'Content-Type,Authorization')
+ res.header('Access-Control-Allow-Credentials', 'true')
+ next()
+})
```
-典型部署:将 `dist/` 目录部署到 CDN 或静态服务器(Nginx、Vercel、Netlify 等)。
+- ✅ 标准方案,任何环境都可用
+- ⚠️ 需要后端配合,不能配置 `*` 时还要处理携带 cookie 的场景
-对于 SPA 应用,需要配置 fallback 到 `index.html`:
+**方案三:Nginx 反向代理(生产环境推荐)**
```nginx
-location / {
- try_files $uri $uri/ /index.html;
-}
-```
+server {
+ listen 80;
+ server_name app.example.com;
----
+ # 前端静态文件
+ location / {
+ root /var/www/dist;
+ try_files $uri /index.html;
+ }
-## 13. 有没有了解微内核设计?Vite 的插件化设计思想是怎样完成个性化打包构建需求的?
-
-### 13.1 什么是微内核架构
-
-微内核(Microkernel)是一种架构模式,核心思想是:**内核只提供最精简的基础能力,所有扩展功能通过插件机制加载**。操作系统领域最经典的例子是 Minix 和 macOS 的 XNU 内核。
-
-在前端构建工具领域,这个思想同样适用:
-
-```
-┌──────────────────────────────────────────┐
-│ 插件层(Plugin Layer) │
-│ Vue SFC │ JSX │ TS │ CSS │ ... │
-├──────────────────────────────────────────┤
-│ 微内核(Minimal Core) │
-│ dev server │ HMR │ module graph │
-│ esbuild预构建 │ 中间件机制 │
-└──────────────────────────────────────────┘
-```
-
-### 13.2 Vite 的微内核设计
-
-Vite 的核心(内核)只做几件事:
-
-| 内核能力 | 说明 |
-|---|---|
-| Dev Server | 基于 connect 的 HTTP 服务器,拦截浏览器请求 |
-| 模块图谱 | 追踪所有模块的依赖关系,驱动 HMR |
-| esbuild 预构建 | CJS → ESM 转换 + 细碎模块合并 |
-| 中间件/钩子系统 | 提供插件接入点,但不做具体编译 |
-
-**Vue SFC 编译、JSX 转换、TypeScript 类型检查、CSS 预处理 —— 这些全部由插件完成,不在内核里。** 这就是微内核思想:内核保持精简稳定,功能由插件按需扩展。
-
-### 13.3 插件机制如何实现个性化构建
-
-Vite 的插件体系基于 **Rollup 插件接口** 并做了增强:
-
-**① 通用钩子(Rollup 兼容层)**
-
-```js
-{
- name: 'my-plugin',
- // 构建开始
- buildStart() {},
- // 模块解析(可拦截 import 路径,实现虚拟模块)
- resolveId(id) {},
- // 模块加载(可返回自定义内容)
- load(id) {},
- // 代码转换(最常用)
- transform(src, id) {}
-}
-```
-
-**② Vite 独有钩子**
-
-```js
-{
- name: 'my-vite-plugin',
- // 配置解析阶段(修改用户配置)
- config(config) {},
- // dev server 启动后(注入自定义中间件)
- configureServer(server) {},
- // 处理 HMR 更新
- handleHotUpdate(ctx) {}
-}
-```
-
-### 13.4 一个完整的个性化构建案例
-
-回到本项目的 `helloPlugin`,它实现了一个个性化需求——「生产环境自动移除 console.log」:
-
-```js
-// vite.config.js
-function helloPlugin() {
- return {
- name: 'helloPlugin',
- // 接入 transform 钩子,拿到每个模块的源码
- transform(src, id) {
- if (!id.includes('/src/')) return null // 只处理业务代码
- if (process.env.NODE_ENV === 'production') {
- const result = src.replace(/console\.log\([^)]*\);?/g, '')
- return { code: result, map: null }
- }
- return null // 不处理则透传
- }
+ # API 代理到后端
+ location /api/ {
+ proxy_pass http://api-server:3000/;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
}
}
```
-这个插件展示了插件化的核心价值:**不需要修改 Vite 源码,不需要 fork 项目,只需写一个函数接入钩子,就能定制打包行为。** 这种「内核不动、插件扩展」的模式,正是微内核架构在前端工程化中的最佳实践。
+- ✅ 前端和后端在同一个域名下,完全不存在跨域
+- ✅ 生产环境的最佳实践
-### 13.5 总结:Vite 的插件化设计思想怎样完成个性化打包构建需求?
+### 26.3 方案选择指南
-一个结构化的回答应包含以下几点:
-
-1. **架构层面**:Vite 采用了**微内核设计**。它的核心非常轻量,仅包含一个 ESM 开发服务器和一套插件加载机制。所有高层功能,如框架支持、CSS 预处理、特定文件加载等,都由独立的插件实现。
-2. **接口层面**:Vite 提供了一套**兼容 Rollup 的、统一的插件接口**。这套接口通过暴露一系列生命周期钩子 (**Hooks**),允许开发者在构建过程的各个关键节点(如配置解析、模块加载、代码转换、HTML 生成等)注入自定义逻辑。
-3. **实践层面**:开发者可以编写一个插件,通过选择合适的钩子来完成特定任务。例如,使用 `transform` 钩子可以支持一种新的语言或文件格式;使用 `configureServer` 钩子可以在开发时添加自定义的服务器中间件;使用 `transformIndexHtml` 则可以动态修改主页面的内容。
+| 阶段 | 推荐方案 | 原因 |
+| -------- | ----------------- | ------------------------------ |
+| 本地开发 | dev server proxy | 零配置、即时生效、真实模拟 |
+| 生产环境 | Nginx 反向代理 | 同域部署,无跨域,性能好 |
+| 跨域 API | CORS + 白名单 | 前后端分开部署时的标准方案 |
+| 临时调试 | 浏览器插件关掉 CORS | ⚠️ 仅限本地调试,绝不用于生产 |
---
-## 14. 站在前端架构角度说说 Bundleless 原理
+## 27. 微前端有哪些实现方案?Module Federation 解决了什么问题?
-### 14.1 什么是 Bundleless
+### 27.1 什么是微前端
-传统的 Bundle 模式(Webpack 为代表)是「先打包再启动」:
+微前端(Micro Frontends)是把一个大型前端应用**拆分为多个独立的小型应用**,每个小型应用可以由不同团队独立开发、测试、部署。
```
-源码 → 打包器(Bundle)→ 一个/多个 bundle 文件 → 浏览器加载
+┌─────────────────────────────────────────────┐
+│ 基座应用(Shell) │
+│ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
+│ │ 子应用 A │ │ 子应用 B │ │ 子应用 C │ │
+│ │ (React) │ │ (Vue) │ │ (Angular) │ │
+│ │ 团队 A │ │ 团队 B │ │ 团队 C │ │
+│ └──────────┘ └──────────┘ └──────────────┘ │
+└─────────────────────────────────────────────┘
```
-Bundleless 模式(Vite 为代表)跳过了打包这一步:
+### 27.2 主流方案对比
-```
-源码 → 开发服务器按需编译 → 浏览器通过 ESM 直接加载
+| 方案 | 原理 | 技术栈限制 | 学习成本 | 适用场景 |
+| ---------------------- | ------------------------------------------ | ---------- | -------- | ---------------------- |
+| **qiankun** | 基于 single-spa,JS 沙箱 + CSS 隔离 | 无限制 | 中 | 遗留系统迁移、多技术栈 |
+| **micro-app** | Web Component + Iframe 思想 | 无限制 | 低 | 快速接入、简单场景 |
+| **wujie(无界)** | Web Component + Iframe + 代理 | 无限制 | 低 | 隔离性要求高的场景 |
+| **Module Federation** | Webpack 5 原生,运行时共享模块 | Webpack 5 | 中高 | 新技术栈、同技术栈 |
+| **Emp** | 基于 Module Federation 的微前端框架 | Webpack 5 | 中 | Module Federation 增强 |
+
+### 27.3 Module Federation 的核心原理
+
+Module Federation 是 Webpack 5 内置的微前端方案,核心思想是**运行时共享模块**:
+
+```js
+// 子应用 A(remote) — webpack.config.js
+new ModuleFederationPlugin({
+ name: 'appA',
+ filename: 'remoteEntry.js',
+ exposes: {
+ './Header': './src/components/Header.vue', // 暴露组件
+ './utils': './src/utils/index.ts' // 暴露工具函数
+ },
+ shared: ['vue', 'vue-router'] // 共享依赖,避免重复加载
+})
+
+// 基座应用(host) — webpack.config.js
+new ModuleFederationPlugin({
+ name: 'host',
+ remotes: {
+ appA: 'appA@http://localhost:3001/remoteEntry.js' // 声明远程模块
+ }
+})
+
+// 基座中使用子应用的组件
+import Header from 'appA/Header' // 就像本地模块一样 import!
```
-### 14.2 为什么 Bundleless 现在才成为主流
+**关键优势**:
-Bundleless 不是新概念——ESM 规范早在 2015 年就发布了。之前无法普及的原因:
+| 对比维度 | qiankun 类方案 | Module Federation |
+| ------------ | --------------------------- | ---------------------------- |
+| 依赖加载 | 每个子应用打包完整的依赖 | 共享依赖(如 Vue 只加载一次)|
+| 组件共享 | 需要额外封装或全局注册 | 原生 import,类型安全 |
+| 构建方式 | 子应用独立构建部署 | 可以独立构建,也可以联合构建 |
+| 运行时性能 | 子应用加载有一定开销 | 几乎零开销 |
-| 时间 | 瓶颈 | 现状 |
-|---|---|---|
-| 2015-2018 | 浏览器 ESM 支持率低(IE 占主流) | IE 已淘汰,现代浏览器全覆盖 |
-| 2015-2019 | npm 包几乎全是 CJS 格式 | 新包逐渐提供 ESM 入口,且有 esbuild 做转换 |
-| 2015-2019 | HTTP/1.1 并发连接有限(6个/域名) | HTTP/2 多路复用,数百个请求不再是瓶颈 |
+### 27.4 微前端的工程化挑战
-**三个条件同时满足——浏览器 ESM 支持、npm 生态转换能力、HTTP/2 普及——Bundleless 才真正可行。**
-
-### 14.3 Bundleless 的架构优势
-
-**① 按需编译,冷启动 O(1)**
-
-传统打包:启动时间 ∝ 项目模块数。Bundleless:只编译浏览器当前请求的文件,启动几乎恒定。
-
-**② HMR 粒度天然匹配 ESM 边界**
-
-每个文件就是一个 ESM 模块,热更新不需要重新打包 bundle,只需要让浏览器重新 import 变更的那个文件。
-
-**③ 开发与生产的明确分工**
-
-```
-开发阶段(Bundleless) 生产阶段(Bundled)
-───────────────────── ─────────────────────
- 不打包,按需编译 Rollup 全量打包
- esbuild 做预构建 Tree-shaking
- ESM 原生加载 代码分割 + 压缩
- 追求开发体验 追求加载性能
-```
-
-这两个阶段的目标本就不同:开发要快,生产要小。Bundleless 把这个分工理清了。
-
-### 14.4 Bundleless 的工程代价
-
-从架构角度,不是所有场景都适合 Bundleless:
-
-| 场景 | Bundleless 是否适用 | 原因 |
-|---|---|---|
-| 现代浏览器开发 | ✅ 完美 | ESM + HTTP/2 |
-| 需要兼容 IE | ❌ 不行 | IE 不支持 ESM |
-| 巨型 node_modules | ⚠️ 需预构建 | 否则请求数爆炸 |
-| 非 JS 资源(CSS/图片) | ⚠️ 需额外处理 | ESM 只管 JS |
-
-这也是为什么 Vite 不是纯 Bundleless——它用预构建和 Rollup 生产打包弥补了 Bundleless 的短板。**务实的设计比纯粹的理念更重要。**
+| 挑战 | 说明 | 应对 |
+| ---------------- | ------------------------------------------ | ------------------------------ |
+| **CSS 冲突** | 多个子应用的全局样式互相覆盖 | CSS Modules / Shadow DOM / qiankun 沙箱 |
+| **JS 全局污染** | 子应用修改了 `window.xxx`,影响其他子应用 | JS 沙箱(qiankun 的 Proxy 沙箱)|
+| **通信机制** | 子应用之间需要共享数据和事件 | 全局 EventBus / 基座下发的 props |
+| **路由管理** | 多个子应用的路由需要统一分配和调度 | 基座统一管理 / 各自的子路由 |
+| **版本管理** | 基座和子应用版本如何协调 | 基座保持向后兼容,子应用可独立升级 |
---
-## 扩展思考:如果你来设计一个类似的工具,会怎么做?
+## 28. 前端如何搭建性能与错误监控体系?
-这通常是大厂面试的进阶追问。可以从以下角度组织回答:
+### 28.1 监控体系全景
-1. **利用浏览器原生能力**:现代浏览器已经支持 ESM、ES2020+ 语法,开发阶段不需要向下兼容。
-2. **语言选择**:高频编译用 Go/Rust 实现的工具(如 esbuild、SWC),生产打包用成熟的 JS 生态工具。
-3. **按需编译**:改变「先打包再启动」的思路,变成「请求时编译」。
-4. **插件兼容**:设计插件接口时尽量对齐已有标准(如 Rollup 插件格式),降低迁移成本。
+```
+前端监控体系
+├── 性能监控
+│ ├── Core Web Vitals(LCP / FID / CLS)
+│ ├── 自定义指标(首屏时间、API 响应时间)
+│ └── 资源加载(慢资源、资源失败)
+├── 错误监控
+│ ├── JS 运行时错误(window.onerror)
+│ ├── Promise 未捕获异常(unhandledrejection)
+│ ├── 框架错误边界(Vue errorHandler / React ErrorBoundary)
+│ └── 接口错误(API 超时、5xx、网络异常)
+└── 行为监控(辅助排查)
+ ├── 用户操作路径(点击、路由跳转)
+ └── 录制回放(Sentry Replay / rrweb)
+```
+
+### 28.2 JS 错误捕获的三种方式
+
+```js
+// ① 全局运行时错误
+window.addEventListener('error', (event) => {
+ reportToSentry({
+ type: 'js-error',
+ message: event.message,
+ filename: event.filename,
+ lineno: event.lineno,
+ colno: event.colno,
+ stack: event.error?.stack
+ })
+})
+
+// ② Promise 未捕获异常
+window.addEventListener('unhandledrejection', (event) => {
+ reportToSentry({
+ type: 'promise-rejection',
+ reason: event.reason
+ })
+})
+
+// ③ 框架级错误边界
+// Vue 3
+app.config.errorHandler = (err, instance, info) => {
+ reportToSentry({
+ type: 'vue-error',
+ message: err.message,
+ stack: err.stack,
+ component: instance?.$options?.name,
+ info
+ })
+}
+```
+
+### 28.3 性能指标采集
+
+```js
+// Core Web Vitals 采集
+import { onLCP, onFID, onCLS, onINP, onTTFB } from 'web-vitals'
+
+onLCP(metric => reportMetric('LCP', metric.value)) // Largest Contentful Paint
+onFID(metric => reportMetric('FID', metric.value)) // First Input Delay
+onCLS(metric => reportMetric('CLS', metric.value)) // Cumulative Layout Shift
+onINP(metric => reportMetric('INP', metric.value)) // Interaction to Next Paint
+
+// 自定义首屏时间(Vue 项目)
+onMounted(() => {
+ nextTick(() => {
+ const fmp = performance.now()
+ reportMetric('FMP', fmp)
+ })
+})
+
+// 资源加载监控
+const observer = new PerformanceObserver((list) => {
+ list.getEntries().forEach(entry => {
+ if (entry.duration > 3000) { // 资源加载超过 3 秒
+ reportSlowResource({ name: entry.name, duration: entry.duration })
+ }
+ })
+})
+observer.observe({ entryTypes: ['resource'] })
+```
+
+### 28.4 常见工具对比
+
+| 工具 | 类型 | 优势 | 适用场景 |
+| -------------- | -------- | ---------------------------------- | ---------------------- |
+| **Sentry** | 自建/云 | 错误聚合 + 堆栈解析 + 源码映射 | **标准选择**,错误监控首选 |
+| **阿里 ARMS** | 云服务 | 性能 + 错误一体化,国内用户友好 | 国内业务、阿里云用户 |
+| **腾讯 TAM** | 云服务 | 免费额度大,与小程序集成好 | 微信生态、小程序 |
+| **LogRocket** | 云服务 | 录制回放,可复现用户操作 | 复杂 Bug 排查 |
+| **自建方案** | 自建 | 可定制、数据安全 | 隐私敏感场景 |
+
+### 28.5 监控的最佳实践
+
+```
+① 错误分级
+ - fatal:页面白屏、无法交互 → 立即告警
+ - error:功能异常但页面可用 → 记录 + 定时汇总
+ - warning:非预期但可降级 → 日志留存
+
+② Source Map 管理
+ - 生产环境不发 .map 到 CDN
+ - .map 只上传到 Sentry 等监控平台
+ - Sentry 会自动根据堆栈找到原始源码位置
+
+③ 采样策略
+ - 错误监控:100% 上报(错误不应该漏)
+ - 性能监控:10-30% 采样(节省成本)
+ - 用户行为:视隐私合规要求决定
+
+④ 告警规则
+ - 5 分钟内错误数 > 100 → 紧急通知
+ - 核心 API 成功率 < 99% → 警告
+ - LCP 超过 4s 的比例 > 20% → 性能预警
+```
+
+---
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 51aa035..275b10a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -68,7 +68,6 @@
"dev": true,
"license": "MIT",
"optional": true,
- "peer": true,
"dependencies": {
"@emnapi/wasi-threads": "1.2.2",
"tslib": "^2.4.0"
@@ -81,7 +80,6 @@
"dev": true,
"license": "MIT",
"optional": true,
- "peer": true,
"dependencies": {
"tslib": "^2.4.0"
}
@@ -891,7 +889,6 @@
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=12"
},
@@ -1001,7 +998,6 @@
"integrity": "sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"lightningcss": "^1.32.0",
"picomatch": "^4.0.4",
@@ -1079,7 +1075,6 @@
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.39.tgz",
"integrity": "sha512-xmZCYabFGcirU8r0fTuvl/LICc1OU620rnqepaJDL/a141ZigkG7AyaxQLdqJ02ZRYzWe6YPaDHeQx7MfknQfA==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@vue/compiler-dom": "3.5.39",
"@vue/compiler-sfc": "3.5.39",
diff --git a/qodana.yaml b/qodana.yaml
new file mode 100644
index 0000000..b7c88e8
--- /dev/null
+++ b/qodana.yaml
@@ -0,0 +1,46 @@
+#-------------------------------------------------------------------------------#
+# Qodana analysis is configured by qodana.yaml file #
+# https://www.jetbrains.com/help/qodana/qodana-yaml.html #
+#-------------------------------------------------------------------------------#
+
+#################################################################################
+# WARNING: Do not store sensitive information in this file, #
+# as its contents will be included in the Qodana report. #
+#################################################################################
+version: "1.0"
+
+#Specify inspection profile for code analysis
+profile:
+ name: qodana.starter
+
+#Enable inspections
+#include:
+# - name:
+
+#Disable inspections
+#exclude:
+# - name:
+# paths:
+# -
+
+#Execute shell command before Qodana execution (Applied in CI/CD pipeline)
+#bootstrap: sh ./prepare-qodana.sh
+
+#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline)
+#plugins:
+# - id: #(plugin id can be found at https://plugins.jetbrains.com)
+
+# Quality gate. Will fail the CI/CD pipeline if any condition is not met
+# severityThresholds - configures maximum thresholds for different problem severities
+# testCoverageThresholds - configures minimum code coverage on a whole project and newly added code
+# Code Coverage is available in Ultimate and Ultimate Plus plans
+#failureConditions:
+# severityThresholds:
+# any: 15
+# critical: 5
+# testCoverageThresholds:
+# fresh: 70
+# total: 50
+
+#Specify Qodana linter for analysis (Applied in CI/CD pipeline)
+linter: jetbrains/qodana-js:2026.1
diff --git a/src/main.js b/src/main.js
index 2070404..8eb9e79 100644
--- a/src/main.js
+++ b/src/main.js
@@ -2,6 +2,7 @@ import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
+console.log(123456)
console.log(123456)
createApp(App).mount('#app')