# 08 — 工程化与质量保障 > 参考:vue-vben-admin(Vite 配置工厂 + lefthook 流水线)、soybean-admin(eslint-config 工厂 + sa CLI)、vue-pure-admin(build/ 拆分 + env 开关能力)、ant-design-pro(Biome + 测试范式 + CI 矩阵) --- ## 一、构建配置组织 ### 1.1 配置组织范式 ``` vben:配置即内部包 internal/vite-config/ 提供 defineConfig({ application | library }) 工厂, app 的 vite.config.ts 只剩 15 行(proxy 声明) pure:根目录 build/ 拆分 build/{plugins,cdn,compress,optimize,utils,info}.ts,vite.config.ts 只做组装 (env 用 wrapperEnv 类型化读取) soybean:build/plugins/ 按域拆文件 {router,unplugin,html,unocss,devtools}.ts 一文件一插件域 ``` 单工程标准:**必须**把 vite 配置按职责拆文件(plugins/env/proxy/compress),主配置只做组装;双栈工程的两个 vite 配置**结构镜像**(插件列表、alias、产物命名、proxy 规则逐项对齐)。 ### 1.2 env 分层(三层结构) | 层 | 形态 | 语义 | 说明 | |----|------|------|------| | 构建期烧录 | `VITE_*` | 打进产物,改了要重新构建 | API 前缀、功能开关 | | 运行时可改 | `VITE_GLOB_*`(vben) | 构建时抽成 `_app-config-{hash}.js` 挂 window | **改 API 地址无需重构建** | | 运行时 JSON | `public/platform-config.json`(pure) | 构建产物旁的配置文件,启动拉取注入 | 免重构建、免 window 挂载 | | 纯 dev | proxy 端口等 | 只在 dev server 用 | — | env 类型契约:每个 `VITE_*` 变量在 `vite-env.d.ts` 有类型与注释(soybean:联合类型直接收窄如 `'static' | 'dynamic'`)。 ### 1.3 dev server 要点(必须) ``` strictPort: true # 端口被占直接报错,不悄悄漂移 proxy '/api' → 后端端口 ws: true # 供 WebSocket(必须显式开) changeOrigin: true ``` ### 1.4 产物与指纹 - 构建注入版本:`define: { __APP_VERSION__, __COMMIT_HASH__ }`(adp 用 git rev-parse 兜底)——设置页「关于」直接展示,排障救命 - 手工分包保留(radix/icons/charts/motion 等);新大依赖(图表/编辑器)**必须**评估独立 chunk + 懒加载 - 可选:构建压缩 env 开关(pure `VITE_COMPRESSION = none|gzip|brotli|both`)、CDN 开关(`VITE_CDN`)、构建时间戳 banner --- ## 二、代码规范流水线 ### 2.1 分层流水线(vben lefthook 范式) ``` pre-commit(仅 staged 文件): 快修复:oxlint --fix → oxfmt → eslint --fix → stylelint --fix (stage_fixed 回填暂存区) commit-msg:commitlint(conventional) push/CI:慢检查全量 vue-tsc/tsc --noEmit + lint + madge 循环依赖 ``` 原则:**快的在本地 pre-commit 自动修,慢的在 CI 卡**。 soybean 的同构版本:`@soybeanjs/eslint-config-vue` 工厂(eslint.config.mjs 仅 14 行)+ `vp lint`(oxc 快扫)→ `eslint`(精修)双层;commit 由自研 `sa` CLI 交互生成并校验,scope 白名单单一数据源。pure 再补两块:stylelint(recess-order 属性排序)与 eslint-plugin-better-tailwindcss(校验 class 书写)。 ### 2.2 必配清单(必须) | 工具 | 要求 | |------|------| | ESLint(flat)+ Prettier | 双端同大版本、规则集可复制 | | Stylelint | standard-scss + recommended-vue + 属性排序 | | commitlint + husky + lint-staged | pre-commit 快修复、commit-msg 校验 | | `preinstall: only-allow pnpm` | 锁包管理器 | **Conventional Commits 类型集**(commitlint type-enum,统一):`feat / fix / docs / style / refactor / perf / test / chore / revert`;scope 用功能域(`user`、`rbac`、`cron`、`react`……)。 **any 管控**:TS strict 端**禁止裸 any**——`as any` 需同行注释原因与消除计划;JS 端以 JSDoc `@param/@returns` 过渡,**不允许**出现「既无类型注释也无类型定义」的接口函数。 ### 2.3 项目级 lint 增强(把各章规则自动化) ```js // eslint 配置片段(示意) 'no-console': ['warn', { allow: ['warn', 'error'] }] // 生产禁 console.log 'no-restricted-imports': // 示例层禁 import 系统层内聚单元 { patterns: [{ group: ['@/views/system/*'], message: '示例层不得依赖系统层' }] } vue/name: 'error' // 页面组件必须 defineOptions name(keep-alive 前置) import/no-cycle: 'error' // 循环依赖 ``` --- ## 三、测试策略 ### 3.1 管理端测试金字塔(务实版) ``` ▲ E2E 冒烟(Playwright) │ 登录→进列表→CRUD→登出 3-5 条主线,别贪多 ├─ 组件/页面测试(Vitest + Testing Library) │ 列表组件三态、表单弹窗校验与提交、菜单失败重试 ├─ 组合函数测试(重点投入区,性价比最高) │ usePagedQuery(取消/错序/分页边界) │ permission 纯函数穷举 │ 请求层拦截器分支(401刷新/重放/静默) └─ 纯函数单测 格式化、confirm、色阶算法 ``` ### 3.2 adp 测试范式(直接照抄) ```ts // 重依赖 mock + 动态 import(vi.mock 提升到顶部,import 在 test 内) vi.mock('@/services/user', () => ({ queryCurrentUser: vi.fn() })) test('未登录访问受保护页重定向 login 并保留 redirect', async () => { queryCurrentUser.mockRejectedValueOnce({ /* 401 */ }) const { getInitialState } = await import('@/app') const redirect = await getInitialState() expect(redirect).toContain('/user/login?redirect=') }) ``` 关键点:**纯函数穷举测试**(permission/access 这类零成本高回报)、**拦截器分支测试**(请求层是事故高发区,mock adapter 驱动各分支)。 ### 3.3 覆盖要求 - coverage 阈值必须配置(如 lines 70 / statements 70 / functions 60 / branches 70),排除生成代码 - E2E 至少覆盖:登录主线、一条 CRUD 主线、模块开关生效(若为底座) - vben 是组件测试深度的标杆:连 Modal 的 keep-alive 场景都有单测 --- ## 四、性能优化清单 | 手段 | 做法 | 出处 | |------|------|------| | 路由懒加载 | 全部页面 `() => import()`;示例层可构建期 tree-shake | 通行 | | 首屏占位 | index.html 注入 loading HTML(#root 空时先渲染骨架) | adp | | 路由预取 | 菜单 hover prefetch / vite `server.warmup` 预热核心目录 | adp/vben/pure | | 图表懒加载 | Suspense/条件 import 包裹 ECharts 区块 | 通行 | | chunk 失效恢复 | ChunkLoadError → 提示/自动 reload 一次(发版后旧 hash 404) | adp + 路由 onError | | 请求防抖取消 | 列表页「取消 + 版本号」(见 03 章) | 本标准 | | 大日期库治理 | moment→dayjs / 按需 locale | adp | | WebSocket | 卸载断开 + 重连指数退避 + 心跳 | 监控类页面 | | 长列表 | 虚拟滚动(日志类页面 >1000 行时) | 按需 | **度量先行**:CI 加 `rollup-plugin-visualizer` 产物报告,chunk 超阈值(如 500KB)报警。 --- ## 五、双端一致性规范(迁移期) ### 5.1 契约清单(迁移开始前统一,不等迁移完成) | 项 | 统一值(推荐) | 说明 | |----|----------------|------| | token key | `.auth.token` | 迁移期「读旧写新」一个版本 | | 暗色 key | `.theme.mode` | 同上 | | 模块开关键 | `.modules..enabled` | 常量两端同名,注释指向后端 API | | 会话存储结构 | 字段名一致 | localStorage 布局对齐 | | 响应契约 | 同一份 code/data/msg + 错误分级 | 见 03 章 | | 权限语义 | 权限码清单同源(单一文档维护) | 见 04 章 | | 路由路径 | 两端一致 | 互通书签 | | ESLint / Prettier | 同大版本、同规则 | 配置可复制 | **执行方式**:契约清单进 `docs/`(一页表),双端各引用;改任何一项 = 双端 PR 同步。副端纪律:不抢先实现主端没有的功能;mock 显式开关、生产禁用;每对齐一个域删除对应 mock(向真实收敛)。 --- ## 六、CI/CD ### 6.1 标准流水线(adp 范式裁剪) ``` PR 触发: job lint eslint + prettier --check + vue-tsc/tsc job test vitest --coverage(阈值卡点) job build vite build job e2e 主分支 PR:playwright(webServer 自动拉起前后端) main 合并: 构建产物部署(嵌入后端二进制 / 静态托管) ``` CI 增强项:madge 循环依赖检查、styles 入口引用检查、产物体积报告。**CI 与本地命令同源**(CI 里跑 make 目标,不另写一套脚本)。 --- ## 七、文档与可观测性 ### 7.1 文档治理(制度) | 文档 | 责任 | |------|------| | 新模块指南 | 新模块五列表(菜单/路由/页面/API/权限码)同步 | | 信息架构文档 | 分组/菜单行为描述与实现同步 | | 删除性重构 | 过期段落归档,不留「描述已删除组件」的漂移文档 | | CHANGELOG | 只描述现存功能 | **规则**:实现与文档不一致 = 文档 bug,同一 PR 修复。 ### 7.2 版本可观测 - 设置页「关于」展示 `__APP_VERSION__` / commit hash / 构建时间(构建指纹注入) - 全局错误上报预留钩子(gva `core/error-handel.js` 式),先 console.error 收口,接 Sentry 留接口 - 可选:页面版本更新提示(pure 的 version-rocket:新版本发布后提醒用户刷新) --- ## 八、最佳实践清单 ✅ **要做** 1. pre-commit 快修复 + CI 慢检查的分层流水线 2. 请求层与权限纯函数优先补测试 3. 双端 vite 配置结构镜像;契约清单一页表维护 4. CI 与本地同源(make 目标) 5. 每次构建带版本指纹,关于页可见 ❌ **不要** 1. 不要双份 e2e/eslint 配置文件并存(配置漂移源头) 2. 不要 CI 跑本地跑不通的命令(或反之) 3. 不要 console.log 进生产(尤其 token 片段) 4. 不要在 mock 数据里放真实用户名/密钥样式的假数据 5. 不要为「以后可能用到」保留死配置、死样式、空目录 --- ## 九、AI 检查清单(对比目标工程逐项核对) **P0(安全与一致性)** 1. 生产路径上是否有 console.log(尤其 token 片段打印)? 2. 双端(若有)token/主题 key 是否同名?配置文件是否双份并存? 3. mock 是否显式开关(是否存在网络错误静默回退假数据)? **P1(质量基建)** 4. lint 流水线是否分层(pre-commit 快修 + CI 慢查)?stylelint/commitlint 是否齐配? 5. 请求层与权限纯函数是否有测试?coverage 阈值是否配置? 6. CI 是否有循环依赖检查、产物体积报告? 7. 构建指纹是否注入并在关于页展示? **P2(体验与治理)** 8. 文档漂移清单(描述已不存在实现的文档段落)列出并归档 9. 运行时配置(VITE_GLOB / platform-config.json)是否值得引入 10. 错误上报钩子是否预留 --- > 至此八章完毕。回到 [00-目录与概览](00-目录与概览.md) 复查十大黄金法则,或按各章「AI 检查清单」逐项对比目标工程输出优化任务。