← 返回场景决策
1. Monorepo vs 多仓库 — 代码组织决策
Monorepo = 大公司一栋楼
→
所有部门在同一栋楼,串门方便,共享设施
多仓库 = 各公司各买各的楼
→
独立自主,但串门要跑很远
✅ 什么时候用 Monorepo
- 共享组件库:UI 组件、工具函数、类型定义 — 改一处所有项目同步
- 全栈同一仓库:前端 + 后端 + 共享类型 — 一次 PR 改前后端
- 多人协作频繁:团队 5-20 人,模块间依赖多 — 统一版本管理
- 原子化提交:前后端接口变更一起提交 — 不出现版本不同步
❌ 什么时候用多仓库
- 团队独立:不同团队完全独立开发 — 各自有自己的节奏
- 代码敏感:核心算法/商业机密 — 不能和普通项目放一起
- 部署独立:不同客户不同部署 — 权限隔离
- 仓库过大:100+ 项目/GB级代码 — Monorepo 工具链压力大
📊 Monorepo vs 多仓库决策表
| 维度 | Monorepo | 多仓库 (Polyrepo) |
| 代码共享 | ✅ 极方便 | 需要发包 |
| 原子提交 | ✅ 一次PR全改 | 多次PR+版本协调 |
| 构建速度 | 需要增量构建 | 独立构建快 |
| 权限控制 | 粗粒度 | 细粒度 |
| CI 复杂度 | 高(影响范围分析) | 低 |
| 适合团队规模 | 5-50人 | 不限 |
| 工具 | pnpm workspace / Turborepo / Nx | 各自为政 |
💻 pnpm workspace Monorepo 配置
// pnpm-workspace.yaml
packages:
- 'apps/*' // 前端应用
- 'packages/*' // 共享库
// 目录结构
// ├── apps/
// │ ├── web/ → 前端项目 (Vue 3 + Vite)
// │ └── admin/ → 管理后台
// ├── packages/
// │ ├── ui/ → 共享 UI 组件库
// │ ├── utils/ → 工具函数
// │ └── types/ → 共享 TypeScript 类型
// └── pnpm-workspace.yaml
// apps/web/package.json 引用本地包
{
"dependencies": {
"@mylib/ui": "workspace:*",
"@mylib/utils": "workspace:*"
}
}
面试金句
pnpm workspace 通过 workspace:* 协议实现本地包引用——符号链接(symlink)而非复制文件,修改 packages/utils 的代码,apps/web 立即生效无需发包。pnpm 的硬链接机制使 node_modules 节省大量磁盘空间(同版本依赖全局存储,各项目硬链接引用)。
2. 微前端 — 大型应用拆分决策
微前端 = 美食广场
→
每个档口独立经营(独立技术栈),顾客统一消费
单体前端 = 一个大工程
→
所有功能在一个代码库,统一管理但项目大了难以维护
✅ 什么时候用微前端
- 巨石应用拆分:100+ 页面的后台系统 — 团队各自负责一个子应用
- 技术栈混合:老系统 jQuery + 新系统 Vue — 渐进式迁移
- 独立部署:各团队独立发布 — 不用等所有团队
- 多团队并行:10+ 前端团队 — 各自为政互不干扰
❌ 什么时候不用微前端
- 小项目:< 30 个页面 — 杀鸡用牛刀,增加大量复杂度
- 同技术栈团队 < 5 人:不需要拆分 — 一个仓库就够
- 强交互场景:跨子应用的拖拽、动画 — 微前端边界是性能和体验的坑
- SEO 要求高:微前端不利于 SEO — 子应用内容爬虫可能看不到
📊 微前端方案决策表
| 方案 | 原理 | JS沙箱 | 样式隔离 | 推荐度 |
| qiankun | HTML Entry + Proxy 沙箱 | ✅ JS沙箱 | ✅ Shadow DOM | ⭐⭐⭐ |
| Module Federation | Webpack 5 模块共享 | ❌ | ❌ | ⭐⭐ |
| single-spa | 路由劫持 | ❌ | ❌ | ⭐⭐ |
| iframe | 浏览器原生隔离 | ✅ 完全隔离 | ✅ 完全隔离 | ⭐ 兼容性差 |
3. Vite vs Webpack — 构建工具决策
Vite = 高铁
→
按需加载,开发秒启动
Webpack = 公交车
→
每站都停,全量编译,稳但慢
📊 Vite vs Webpack 决策表
| 维度 | Vite | Webpack |
| 开发启动 | 毫秒级(ESM) | 几十秒起(全量打包) |
| HMR 热更新 | 毫秒级 | 秒级 |
| 构建原理 | 开发时 ESM + 生产时 Rollup | 全量打包 |
| 生态/插件 | 丰富但不如 Webpack | 最丰富 |
| 配置复杂度 | 极简 | 复杂 |
| CSS 预处理 | ✅ 开箱即用 | 需要 loader 链 |
| Vue 支持 | 官方推荐 | 需要 vue-loader |
| 兼容性 | ESM 浏览器 | IE11(旧版) |
| 推荐度 | ⭐⭐⭐ 新项目首选 | 维护旧项目 |
⚠️ 选型建议:2024+ 新项目一律用 Vite。旧 Webpack 项目如果运行良好不要迁移,改构建工具性价比低。
4. CI/CD — 持续集成与部署决策
CI = 流水线质检
→
每件产品出厂前自动检测
CD = 快递自动发货
→
质检通过自动发货,不用人工打包
✅ 什么时候需要 CI/CD
- 团队协作:2+ 人开发 — 自动跑 lint/test,防止低级错误合入
- 频繁发布:每天/每周发布 — 自动化构建部署
- 多环境:dev/staging/prod — 自动部署到对应环境
- 质量保障:PR 合入前自动跑测试 — 不通过不准合
📊 CI/CD 方案决策表
| 工具 | 托管 | 适合 | 价格 |
| GitHub Actions | GitHub | 开源项目 + GitHub 生态 | 免费额度大 |
| GitLab CI | GitLab | 私有部署 + 内网 | 自建免费 |
| Jenkins | 自建 | 复杂流水线 + 大团队 | 免费 |
| Vercel | SaaS | 前端项目极速部署 | 免费额度 |
💻 GitHub Actions 配置示例
# .github/workflows/ci.yml
name: CI
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with: { version: 8 }
- uses: actions/setup-node@v4
with: { node-version: 20, cache: 'pnpm' }
- run: pnpm install
- run: pnpm lint # 代码规范检查
- run: pnpm test # 单元测试
- run: pnpm build # 构建验证
面试金句
GitHub Actions 的 cache: pnpm 缓存 node_modules,将安装时间从 60s+ 降至 5s。CI 流水线标准四步:lint(代码规范)→ test(单元测试)→ build(构建验证)→ deploy(部署)。PR 合入前必须 CI 全绿,防止主分支被破坏。
5. 自动化测试 — 测试策略决策
测试金字塔 = 饮食金字塔
→
底层多(单元测试),顶层少(E2E),健康均衡
📊 测试类型决策表
| 类型 | 范围 | 速度 | 维护成本 | 覆盖场景 | 推荐比例 |
| 单元测试 | 函数/组件 | 毫秒 | 低 | 工具函数、计算逻辑 | 70% |
| 集成测试 | 模块交互 | 秒级 | 中 | 组件 + Store + API | 20% |
| E2E 测试 | 完整流程 | 分钟级 | 高 | 关键业务流程 | 10% |
✅ 什么必须测试
- 核心业务逻辑:价格计算、折扣规则、支付流程 — 错了就是钱
- 工具函数:日期格式化、数据转换 — 纯函数最好测
- API 层:接口参数、错误处理 — 防止接口变更漏改
❌ 什么不必测试
- 第三方库本身:不要测 Vue 的响应式 — 人家自己有测试
- 纯 UI 展示:颜色、字体大小 — 视觉回归测试更合适
- 简单 CRUD:增删改查如果没特殊逻辑 — ROI 低
💻 Vitest 单元测试示例
// utils/price.test.ts
import { describe, it, expect } from 'vitest';
import { calcDiscount } from './price';
describe('calcDiscount', () => {
it('正常折扣', () => {
expect(calcDiscount(100, 0.8)).toBe(80);
});
it('折扣为0时返回0', () => {
expect(calcDiscount(100, 0)).toBe(0);
});
it('负数价格抛错', () => {
expect(() => calcDiscount(-1, 0.8)).toThrow();
});
});
面试金句
Vitest 是 Vite 原生测试框架,与 Vite 共享配置和转换管线(Jest 需要独立配置)。测试金字塔原则:70% 单元测试(快、维护低)+ 20% 集成测试 + 10% E2E。纯函数(无副作用)是最好测试的——输入确定输出,不需要 mock。
6. XSS 防御 — 跨站脚本攻击
XSS = 在你家里贴假通知
→
坏人把恶意脚本混在正常内容里,浏览器傻傻执行
⚠️ XSS 三种类型
- 存储型 XSS:恶意代码存入数据库 → 所有用户浏览时执行 — 最危险
- 反射型 XSS:恶意代码在URL参数中 → 点击链接时执行 — 钓鱼常用
- DOM型 XSS:前端 JS 直接操作 DOM 插入恶意代码 — 不经过服务器
✅ XSS 防御方案
- 输入转义:所有用户输入展示前转义 < > & " ' — 最基本
- v-html 禁用:Vue 中不用 v-html,用 {{ }} 自动转义 — 除非信任内容
- Content-Security-Policy:限制脚本来源 — 只允许自己域名的脚本
- HttpOnly Cookie:JS 读取不到 Cookie — 即使 XSS 也偷不到
- DOMPurify:需要渲染 HTML 时用 DOMPurify 清洗 — 白名单过滤
💻 XSS 防御代码
// 1. Vue 中自动转义(默认安全)
// ✅ {{ userInput }} — 自动转义
// ❌ v-html="userInput" — 危险!
// 2. 必须渲染HTML时用 DOMPurify
import DOMPurify from 'dompurify';
const safeHTML = DOMPurify.sanitize(userRichText);
// 过滤掉 <script>、onerror 等,保留安全的 HTML 标签
// 3. URL 参数转义
const safeURL = encodeURIComponent(userInput);
// 4. Spring Boot 后端转义
// 依赖: org.unbescape:unbescape
String safe = HtmlEscape.escapeHtml4(userInput);
// 5. Cookie 设置 HttpOnly
// Spring Boot
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer s = new DefaultCookieSerializer();
s.setCookieHttpOnly(true); // JS 无法读取
return s;
}
面试金句
XSS 防御分层:① Vue {{ }} / React {} 自动 HTML 转义(默认安全);② 需要渲染富文本时用 DOMPurify.sanitize() 白名单过滤(保留安全标签,移除 script/onerror);③ HttpOnly Cookie 防止 JS 读取 Cookie(即使 XSS 也偷不到会话凭证);④ CSP 限制脚本来源。永远不要用 v-html 渲染用户输入。
7. CSRF 防御 — 跨站请求伪造
CSRF = 有人冒充你签字
→
你在A站登录了,坏人诱骗你访问B站,B站偷偷发请求给A站
✅ CSRF 防御方案
- CSRF Token:每次请求带随机 Token — 服务器验证 Token 一致性
- SameSite Cookie:Set-Cookie: SameSite=Strict — 跨站请求不带 Cookie
- 验证 Referer/Origin:检查请求来源 — 简单但不完全可靠
- JWT + Authorization Header:Token 放 Header 不是 Cookie — 天然防 CSRF
📊 CSRF 防御方案决策表
| 方案 | 原理 | Cookie认证 | Token认证 | 推荐度 |
| CSRF Token | 表单/请求头带随机数 | ✅ 必须 | 不需要 | ⭐⭐⭐ |
| SameSite Cookie | 浏览器不发送跨站Cookie | ✅ 简单 | 不需要 | ⭐⭐⭐ |
| Referer 检查 | 验证请求来源 | 辅助 | 不需要 | ⭐⭐ |
| 用 Token 认证 | 不用 Cookie | - | 天然免疫 | ⭐⭐⭐ |
💻 Spring Security CSRF 配置
// Spring Security 默认开启 CSRF
// 表单提交时需要带 _csrf 参数
// 前端 Axios 自动带 CSRF Token
axios.defaults.xsrfCookieName = 'XSRF-TOKEN';
axios.defaults.xsrfHeaderName = 'X-XSRF-TOKEN';
// 如果用 JWT Token 认证,可以关闭 CSRF(因为不用 Cookie)
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http.csrf(csrf -> csrf.disable()); // JWT 时关闭
}
}
面试金句
CSRF 攻击利用的是浏览器自动携带 Cookie 的机制——恶意网站构造 POST 请求到银行 API,浏览器自动带上用户的 Session Cookie。防御核心:JWT Token 放 Authorization Header(非 Cookie)天然免疫 CSRF。如果用 Cookie 认证,必须配 SameSite=Strict 或 CSRF Token。
8. CSP — 内容安全策略
CSP = 门禁白名单
→
只有名单上的人能进,名单外的全拦
✅ 什么时候启用 CSP
- 生产环境必开:所有上线项目 — 防止 XSS 注入的最后一道防线
- 有用户生成内容:评论、文章、备注 — 用户可能注入恶意脚本
- 第三方SDK多:接入各种第三方 — 需要限制脚本来源
💻 CSP 配置
// Nginx 配置 CSP Header
add_header Content-Security-Policy "
default-src 'self';
script-src 'self' https://cdn.example.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://api.example.com;
font-src 'self' https://fonts.googleapis.com;
frame-src 'none';
";
// Spring Boot 配置
@Bean
public WebSecurityCustomizer cspCustomizer() {
return web -> web.httpStrictTransportSecurity()
.addHeaderWriter(new ContentSecurityPolicyHeaderWriter(
"default-src 'self'; script-src 'self'"
));
}
面试金句
CSP(Content Security Policy) 是 XSS 防御的最后一道防线:通过 HTTP 头声明资源加载白名单(script-src/style-src/img-src),浏览器拒绝加载不在白名单的资源。default-src 'self' 是最严格策略——只允许同源资源。部署策略:先用 Content-Security-Policy-Report-Only(仅报告不拦截)收集违规,再切换到强制模式。
⚠️ CSP 常见坑:Report-Only 模式先观察 → 确认没有误拦截 → 再切到强制模式。直接上强制 CSP 可能导致页面功能异常。
9. Git 规范 — 协作流程决策
📊 Git 分支策略决策表
| 策略 | 分支模型 | 适合 | 复杂度 |
| GitHub Flow | main + feature/* | 持续部署项目 | 低 |
| Git Flow | main + develop + feature/* + release/* + hotfix/* | 版本发布项目 | 高 |
| Trunk Based | main + 短命 feature 分支 | 高频发布 + CI/CD 成熟 | 中 |
💻 Commit 规范 + husky
// Commit Message 格式
// <type>(<scope>): <subject>
//
// type:
// feat: 新功能
// fix: 修复bug
// docs: 文档
// style: 格式(不影响逻辑)
// refactor: 重构
// perf: 性能优化
// test: 测试
// chore: 构建/工具
//
// 示例:
// feat(订单): 新增批量导出功能
// fix(登录): 修复Token过期后无限刷新
// perf(列表): 虚拟滚动优化大数据渲染
// 安装 commitlint + husky
// npm install -D @commitlint/cli @commitlint/config-conventional husky
// npx husky init
// echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg
// commitlint.config.js
export default {
extends: ['@commitlint/config-conventional']
};
面试金句
Conventional Commits 规范(feat/fix/docs/refactor/perf/test/chore)让 commit message 机器可读,配合 commitlint + husky 在 git commit 时自动校验格式。自动生成 CHANGELOG:standard-version / semantic-release 根据 commit type 自动语义化版本号和变更日志。scope 用于标识影响范围,方便 code review 和 blame 定位。