引言
「页面变慢了」是结果,不是原因。真正的问题在于:包体为什么大、首屏为什么慢、谁在拖慢构建——这些需要「分析 + 监控 + 门禁」的体系,而不是靠感觉优化。本文讲 Vite 项目的包体分析与性能监控:先讲包体分析的价值(把「感觉」变成「数据」)、Bundle Analyzer 可视化产物(占比一目了然)、构建报告与产物统计(体积/请求/重复)、性能预算与 CI 门禁(设阈值、超限即失败,防回退)、运行时性能监控(Web Vitals/错误监控/性能数据采集)、加载性能优化闭环(分析→优化→验证→门禁)、长尾模块与按需加载(找大而用的少模块)、慢构建的定位(构建耗时与缓存)、最后是性能治理流程(从临时优化到制度化、可量化的性能文化)。
前置:/vite-build-optimization/(构建优化)、/vite-runtime-performance-optimization/(运行时性能)、/vite-ci-cd-optimization/(CI/CD 优化)。
目录
- 1. 包体分析的价值
- 2. Bundle Analyzer:可视化产物
- 3. 构建报告与产物统计
- 4. 性能预算与 CI 门禁
- 5. 运行时性能监控
- 6. 加载性能优化闭环
- 7. 长尾模块与按需
- 8. 慢构建的定位
- 9. 性能治理流程
- 10. 速查表与一句话记忆
- 延伸阅读
1. 包体分析的价值
性能优化的前提 = 可量化:
「感觉包很大」→ 数据:gzip 1.2MB
「感觉首屏慢」→ 数据:LCP 4.5s
「感觉构建慢」→ 数据:build 82s
→ 分析让「感觉」变成「可比较的指标」
为什么需要包体分析:
1. 定位大模块:包里哪个依赖/文件占大头
2. 发现异常:重复模块/未摇树/误引入
3. 制定预算:设阈值(防无限膨胀)
4. 验证优化:改前改后对比(是否真瘦了)
→ 分析 = 定位 + 发现 + 预算 + 验证
包体分析的指标:
- 总包体(gzip/brotli 传输体积)
- 首包(入口相关 chunk 和,最关键)
- 最大 chunk / chunk 数量
- 重复模块(同依赖多 chunk)
- 树图占比(哪个模块最大)
→ 指标 = 总量 + 首包 + 分布 + 重复
什么时候做分析:
- 构建后例行(每次 CI)
- 性能优化前(先摸底再动手)
- 依赖升级/新增后(包体是否暴涨)
- 性能回退时(对比历史基线)
→ 时机 = 例行 + 优化前 + 依赖变更 + 回退时
分析工具的角色:
- 可视化(human 视角):占比/分布一眼看
- 报告(机器视角):指标/阈值/门禁
- 两者结合:可视化找问题、报告守门禁
→ 工具 = 可视化 + 报告双角色
心智:包体分析让性能优化「可量化」:把感觉变数据(gzip 1.2MB/LCP 4.5s/构建 82s);价值四用:定位大模块、发现异常(重复/未摇树/误引入)、制定预算防膨胀、验证优化(改前改后对比);指标 = 总量 + 首包 + 分布 + 重复;时机 = 例行 CI + 优化前摸底 + 依赖变更 + 回退时;工具双角色:可视化找问题、报告守门禁。
2. Bundle Analyzer:可视化产物
rollup-plugin-visualizer:
npm i -D rollup-plugin-visualizer
// vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({
plugins: [
visualizer({
open: true, // 构建后自动打开
gzipSize: true, // 显示 gzip 体积
filename: 'stats.html'
})
]
})
可视化的视图:
- 树状图(Treemap):占比方块(最大模块一眼见)
- 圆形图(Sunburst):依赖嵌套层级
- 网络图:chunk 依赖关系
- 列表:按体积排序的模块明细
→ 视图 = 树图(占比)+ 环形(嵌套)+ 网络(依赖)+ 列表
visualizer 的读法:
- 找最大方块:最大的依赖/文件(优化目标)
- 看 gzip 与原始:压缩比(哪些可优化)
- 看 chunk 分布:首包含哪些(该分割的没分割)
- 查重复:同模块多 chunk(需提取)
→ 读法 = 最大方块 + 压缩比 + 首包分布 + 重复
其他分析工具:
- vite build --report(报告输出)
- source-map-explorer:按 sourcemap 分析
- bundle-buddy:依赖对分析(chunk 拆分建议)
- 自定义脚本:解析产物 JSON 做统计
→ 工具矩阵 = visualizer 主 + report/explorer 辅
分析大包体的常见结论:
- 误引入:import 了大库(moment/lodash 全量)
- 未摇树:CJS 依赖/副作用(见摇树篇)
- 重复打包:同库多版本/多 chunk
- 未分割:大组件/路由在首包
- 多余依赖:用一次的功能带整个库
→ 结论 = 误引/未摇/重复/未分/多余 五类
可视化与门禁的配合:
- 可视化:人工分析「为什么大」(优化动作)
- 报告:机器检查「是否超预算」(门禁)
- 流程:可视化发现 → 优化 → 报告验证
→ 配合 = 人看问题、机器守线
心智:rollup-plugin-visualizer 让产物可视化(树图占比/环形嵌套/网络依赖/列表排序,开 gzipSize);读法四看:最大方块(优化目标)、压缩比、首包分布(该分割没分割)、重复模块;其他工具:vite build –report、source-map-explorer、自定义脚本;大包体五大结论:误引入、未摇树、重复打包、未分割、多余依赖;配合 = 可视化人工发现 + 报告机器守门禁。
3. 构建报告与产物统计
构建报告(vite build –report):
vite build --report
→ 输出产物统计报告(体积/请求等)
→ 或配 rollupOptions.output + 自定义
产物统计的自定义脚本:
// scripts/analyze-size.mjs
import { readFileSync } from 'node:fs'
import { gzipSync } from 'node:zlib'
const dir = 'dist/assets'
const files = readFileSync(dir, { encoding: 'utf-8' })
// 统计每个 chunk 的原始/gzip 体积
// 汇总:总包、首包、最大 chunk、chunk 数
统计的核心指标:
- 总原始体积(未压缩)
- 总 gzip/brotli 体积(传输)
- 首包:入口 chunk + 其依赖(同步加载)
- chunk 数 / 请求数(HTTP 开销)
- 最大 chunk / 最小 chunk(分布)
→ 指标 = 总量 + gzip + 首包 + 请求数
首包的定义与计算:
- 首包 = 首屏「同步加载」的所有 chunk
(入口 chunk + 它 import 的静态依赖)
- 动态导入(懒加载)不在首包
- 计算:从入口 chunk 沿「静态 import」收集
- 优化目标:首包最小化(首屏体验)
→ 首包 = 入口 + 静态依赖链,动态排除
产物统计的对比:
- 基线:上次构建的统计(历史记录)
- 对比:本次 vs 基线(变化 + 回退检测)
- 趋势:多次构建的体积曲线(膨胀监控)
- 报告存储:CI artifact(可追溯)
→ 对比 = 基线 + 差异 + 趋势 + 存档
报告的实际用途:
- 发布前检查(体积达标)
- 性能审查(回退分析)
- 依赖管理(新增依赖的体积影响)
- 门禁输入(第 4 节阈值判断)
→ 用途 = 检查 + 审查 + 依赖影响 + 门禁
心智:构建报告 = vite build –report 或自定义脚本统计产物;核心指标:总原始/gzip 体积、首包(入口+静态依赖链,动态排除)、chunk 数/请求数、最大 chunk;首包 = 首屏同步加载的 chunk 和(优化目标);对比 = 基线 + 差异 + 趋势曲线 + CI artifact 存档;用途:发布检查、回退审查、依赖体积影响、门禁输入。
4. 性能预算与 CI 门禁
性能预算 = 设阈值的承诺:
预算示例:
首包 gzip < 180KB
总包 gzip < 800KB
构建时间 < 60s
LCP < 2.5s(运行时)
→ 预算 = 「性能的红线」,超线即失败
预算的制定:
- 基于基线:当前实际 + 余量(防立即失败)
- 分项预算:首包/总包/构建时间(不同维度)
- 按业务:核心页面严格、次要宽松
- 可调整:随优化进步收紧(渐进)
→ 制定 = 基线 + 余量 + 分项 + 渐进
CI 门禁的实现:
// package.json
"scripts": {
"build": "vite build",
"check-bundle": "vite build && node scripts/check-budget.mjs"
}
// scripts/check-budget.mjs
// 解析构建产物体积 → 与预算阈值比较
// 超预算:process.exit(1)(CI 失败)
// 未超:通过(可生成报告)
门禁的集成:
- CI 管道:build → check-bundle → deploy
- 超限:CI 失败(阻塞发布)
- 看板:门禁结果可视化(趋势)
- 例外:临时豁免(标注原因 + 期限)
→ 集成 = 构建后检查 + 失败阻塞 + 看板 + 例外
门禁的常见设计:
- 严格门禁:超限即失败(防回退强)
- 宽松门禁:警告不失败(允许有例外)
- 分级:不同分支不同预算(主分支严)
- 预算文件:budget.json 集中配置
→ 设计 = 严格/宽松 × 分级 × 配置化
门禁防回退的机制:
- 依赖升级暴涨 → 门禁拦截(提示审查)
- 误引入大库 → 门禁拦截(定位回退)
- 优化后改善 → 收紧预算(持续进步)
- 历史对比 → 门禁看趋势(防缓慢膨胀)
→ 机制 = 拦截暴涨 + 定位回退 + 收紧进步 + 趋势监控
心智:性能预算 = 红线承诺(首包 gzip<180KB/总包<800KB/构建<60s/LCP<2.5s);制定 = 基线+余量+分项+渐进收紧;CI 门禁 = build → check-budget 脚本(解析产物 vs 阈值 → 超限 exit(1))→ 部署;集成 = 构建后检查 + 失败阻塞发布 + 看板趋势 + 例外豁免(标注原因期限);设计:严格/宽松 × 分级(主分支严)× budget.json 配置化;机制 = 拦截依赖暴涨、定位回退、收紧进步、趋势防缓慢膨胀。
5. 运行时性能监控
Web Vitals(核心指标):
LCP(Largest Contentful Paint):最大内容绘制(加载)
目标 < 2.5s
INP(Interaction to Next Paint):交互响应
目标 < 200ms
CLS(Cumulative Layout Shift):布局偏移
目标 < 0.1
→ 加载/交互/稳定 = 用户体验三维
Web Vitals 的采集:
// 在应用里采集(web-vitals 库)
import { onLCP, onINP, onCLS } from 'web-vitals'
onLCP(metric => sendToAnalytics(metric))
onINP(metric => sendToAnalytics(metric))
onCLS(metric => sendToAnalytics(metric))
// 上报到监控平台(RUM)
性能监控平台(RUM):
- 自建:采集 → 上报 → 聚合 → 看板
- 现成:Cloudflare RUM / Sentry Performance
/ 阿里云 ARMS / 自研
- 数据:Vitals + 资源加载 + 错误
- 看板:趋势/分位数/按页面
→ RUM = 采集真实用户性能数据
资源加载监控:
- 资源时间:script/style/img 加载耗时
- 请求瀑布:首屏资源顺序(瓶颈定位)
- 大资源:超阈值资源告警(图/脚本)
- 慢请求:API 响应时间(数据层瓶颈)
→ 加载 = 资源耗时 + 瀑布 + 大资源 + 慢请求
错误与性能的关联:
- JS 错误:导致功能不可用(间接性能差)
- 资源失败:404/超时(CDN/路径问题)
- 慢接口:首屏等待(后端瓶颈)
- 异常采集:错误率 + 影响用户数
→ 错误 = 性能差的「隐藏根源」
性能数据的决策:
- 分位数:P75/P95(反映真实体验而非平均)
- 按页面/路由:定位问题页面
- 按设备/网络:低端设备/弱网表现
- 对比发布前后:版本性能回退检测
→ 决策 = 分位 + 页面 + 设备 + 版本对比
心智:运行时监控 = Web Vitals(LCP<2.5s 加载/INP<200ms 交互/CLS<0.1 稳定)用 web-vitals 库采集上报 RUM 平台(Cloudflare/Sentry/自研);资源加载监控:资源耗时/请求瀑布/大资源告警/慢接口;错误监控:JS 错误/资源失败/慢接口(性能差的隐藏根源);决策看分位数 P75/P95(非平均)+ 按页面/设备/网络 + 版本发布前后对比检测回退。
6. 加载性能优化闭环
加载性能 = 首屏能多快看:
影响首屏的因素:
包体(传输)→ 请求数(并发)→ 解析执行(CPU)→ 渲染(内容可见)
优化点:
包体:分割/摇树/压缩
请求:预加载/合并/HTTP 缓存
解析:减少主线程负担(按需/拆分)
渲染:关键 CSS 内联/骨架屏
→ 首屏 = 传输 + 请求 + 解析 + 渲染 全链路
优化闭环(DORA 循环):
1. 测量:建立基线(Vitals/包体)
2. 定位:Analyzer/瀑布找瓶颈
3. 优化:针对瓶颈(分割/预取/缓存)
4. 验证:对比基线(是否改善)
5. 门禁:预算 CI 防回退
→ 闭环 = 测 → 定 → 优 → 验 → 守
常见的加载优化手段:
- 代码分割:路由懒加载(首包小)
- Tree-shaking:删未用导出
- 资源优化:图片压缩/字体子集/懒加载图片
- 预加载:关键资源 preload、动态 chunk 预取
- HTTP 缓存:内容哈希 + Cache-Control
- CDN:边缘分发(地域就近)
→ 手段 = 分割 + 摇树 + 资源 + 预取 + 缓存 + CDN
首屏的关键优化顺序:
1. 先砍「首包体积」(分割 + 摇树)——影响最大
2. 再优「请求策略」(preload/缓存/CDN)
3. 后调「渲染体验」(关键 CSS/骨架屏)
4. 最后「运行时」(交互响应,见运行性能篇)
→ 顺序 = 体积 → 请求 → 渲染 → 交互
验证优化效果:
- 对比改前改后:Vitals + 包体
- A/B:不同优化方案对比
- 实验室 vs 真实:Lighthouse(lab)/RUM(field)
- 回归:改动是否引入性能回退
→ 验证 = 前后对比 + A/B + 实验室/真实 + 回归
优化的权衡:
- 分割 vs 请求数:太碎请求多
- 预取 vs 首屏:预取太多首屏忙
- 压缩 vs 兼容:低 target 体积大
- 缓存 vs 更新:长缓存更新慢
→ 权衡 = 每一项都要平衡而非极端
心智:加载性能 = 首屏全链路(传输包体→请求数→解析执行→渲染),首屏优化顺序:先砍首包体积(分割+摇树影响最大)→ 优请求策略(preload/HTTP 缓存/CDN)→ 调渲染体验(关键 CSS/骨架屏)→ 交互响应;优化闭环 = 测量基线 → Analyzer/瀑布定位 → 针对优化 → 对比验证 → 预算门禁防回退;验证 = 前后对比 + A/B + Lighthouse(lab)/RUM(field);权衡:分割 vs 请求、预取 vs 首屏、压缩 vs 兼容、缓存 vs 更新都要平衡。
7. 长尾模块与按需
长尾模块:大而用得少:
长尾特征:
- 体积大(占包体比例高)
- 使用频率低(只在特定页面/路径)
- 进首包 → 白白拖慢所有人
例:管理后台的图表库(只在报表页用)
→ 全量进首包 = 浪费
→ 长尾 = 大体积 × 低频率 × 不该进首包
长尾的识别:
- Analyzer:大模块占比(第 2 节)
- 使用频率:埋点/代码分析(哪个路由用)
- 首包检查:大模块是否在首包(不该在)
- 依赖分析:该依赖是否只服务一个功能
→ 识别 = 体积 × 使用频率 × 首包归属
长尾的按需化:
1. 懒加载:动态 import 长尾模块(路由/组件级)
2. 子路径:大库按子路径引入(如 lodash/map)
3. 按需库:换按需版本(如 antd 按需)
4. 拆包:重组件独立 chunk(用到才加载)
→ 按需 = 懒加载 + 子路径 + 按需库 + 拆包
识别并处理长尾的流程:
1. Analyzer 找大模块
2. 查使用频率(是否只在某页)
3. 若低频 → 懒加载/子路径优化
4. 验证:该模块移出首包(体积下降)
5. 门禁:监控该模块不再回首包
→ 流程 = 找 → 查频 → 优化 → 验证 → 门禁
按需的常见误区:
- 过度按需:连核心功能也拆(首屏请求爆炸)
- 误拆共享:多页共用的拆了(重复加载)
- 忘记预取:拆了但切换时慢(要预取)
- 只减体积不减体验:gzip 降但首屏没快
→ 误区 = 过度拆 + 误拆共享 + 忘预取 + 只减数字
按需与体验的平衡:
- 长尾:懒加载(低频不拖累)
- 高频但大:预取/提前(常用要快)
- 核心:留在首包(不拆)
- 折中:预取临界(hover/即将进入)
→ 平衡 = 按「频率 × 体积」决定加载策略
心智:长尾模块 = 大体积 × 低频率 × 不该进首包(如报表页的图表库);识别 = Analyzer 占比 + 使用频率 + 首包归属;按需化四法:懒加载(动态 import)、子路径(lodash/map)、按需库版本、拆包独立 chunk;流程 = 找大模块 → 查频率 → 低频优化 → 验证移出首包 → 门禁防回归;误区:过度拆(请求爆炸)、误拆共享(重复加载)、忘预取(切换慢)、只减数字不减体验;平衡 = 低频懒加载、高频预取、核心留首包、临界预取。
8. 慢构建的定位
构建慢的常见来源:
1. transform:每模块跑插件链(大头)
2. 依赖转译:node_modules 处理
3. 压缩:minify 大文件
4. 资源:大图片/字体处理
5. 插件:第三方插件耗时
→ 慢源 = transform + 依赖 + 压缩 + 资源 + 插件
定位构建耗时:
# 构建 debug 时间
DEBUG=vite:build vite build
# 观察每个阶段的耗时输出
# 或 vite build --profile(CPU 分析)
# 或插件的 transform 计时(第 4 节)
transform 慢的优化:
- 缓存:转换缓存(未变化模块复用)
- 减少插件:删不必要的 transform 插件
- esbuild 优先:不用 Babel 的用 esbuild 转译
- 并行:多核并行转换
→ transform = 缓存 + 减插件 + esbuild + 并行
依赖转译慢的优化:
- 预打包:依赖提前打包(esbuild 一次)
- 缓存:.vite 缓存复用(不重复转)
- 排除:exclude 大依赖(若不需要预打包)
- 升级:新依赖版本(构建性能更好)
→ 依赖 = 预打包 + 缓存 + exclude + 升级
压缩与资源慢的优化:
- 压缩:esbuild(快)vs terser(慢但兼容)
→ 能接受产物则用 esbuild minify
- 资源:大图片压缩/缓存资源处理
- 跳过:不需要的 sourcemap(production 可选)
- 并行:多 chunk 并行生成
→ 压缩 = esbuild 快 / 资源压缩 / 减 map / 并行
构建优化的权衡:
- 快 vs 小:esbuild 快但产物可能略大
- 缓存 vs 干净:缓存快但可能陈旧
- 并行 vs 内存:并行快但吃内存
- CI 缓存:共享缓存加速 CI 构建
→ 权衡 = 速度 × 产物质量 × 资源
心智:慢构建五源:transform(大头,缓存+减插件+esbuild+并行)、依赖转译(预打包+.vite 缓存+exclude)、压缩(esbuild 快 vs terser 慢)、资源(图片压缩/减 sourcemap)、插件耗时;定位 = DEBUG=vite:build + –profile + 插件计时;权衡:快 vs 小(esbuild)、缓存 vs 干净、并行 vs 内存、CI 共享缓存。
9. 性能治理流程
从临时优化到制度化:
阶段 1:救火(临时)——发现慢就优化一次
阶段 2:度量(量化)——建立指标与基线
阶段 3:门禁(防回退)——CI 预算拦截
阶段 4:文化(制度化)——性能是默认要求
→ 治理 = 从「救火」进化到「文化」
性能治理的组成:
- 指标:Vitals + 包体 + 构建(可量化)
- 基线:历史数据(可对比)
- 预算:阈值(可拦截)
- 流程:优化闭环(可迭代)
- 归属:性能负责人(有人负责)
→ 治理 = 指标 + 基线 + 预算 + 流程 + 归属
性能负责人与 SLA:
- 角色:性能负责人(谁牵头/审查/跟进)
- SLA:性能目标(首包 < 180KB/LCP < 2.5s)
- 评审:性能审查(大改动前评估)
- 跟进:回退处理(谁负责修)
→ 治理 = 明确负责人 + SLA + 审查 + 跟进
性能文化的建立:
- 默认要求:新功能默认考虑性能(不塞首包)
- 审查清单:PR 性能检查项
- 学习共享:性能优化案例分享
- 工具普及:Analyzer/监控人人会用
→ 文化 = 默认 + 审查 + 学习 + 工具
治理的落地节奏:
1. 建立基线:跑一轮 Vitals + Analyzer
2. 设第一批预算:首包 + LCP(最影响)
3. 接入门禁:CI 检查(防回退)
4. 补监控:RUM 采集(真实数据)
5. 制度化:审查清单 + 负责人 + SLA
→ 节奏 = 基线 → 预算 → 门禁 → 监控 → 制度
治理的常见失败:
- 只设预算不执行(门禁形同虚设)
- 只有数字不看体验(LCP 降但用户没感觉)
- 过度优化(微优化不值得)
- 无人负责(回退没人修)
→ 失败 = 不执行 + 不看体验 + 过度 + 无归属
心智:性能治理四阶段:救火(临时)→ 度量(指标基线)→ 门禁(CI 拦截)→ 文化(性能是默认要求);组成 = 指标 + 基线 + 预算 + 优化闭环 + 负责人;SLA 明确目标 + 性能审查 + 回退跟进归属;文化 = 新功能默认考虑 + PR 性能检查 + 案例共享 + 工具普及;落地节奏 = 基线 → 首包/LCP 预算 → CI 门禁 → RUM 监控 → 审查清单/负责人/SLA 制度化;失败四因:不执行、不看体验、过度优化、无归属。
10. 速查表与一句话记忆
全篇速查:
| 主题 | 结论 |
|---|---|
| 分析价值 | 把「感觉」变「数据」 |
| Analyzer | visualizer 树图定位大模块 |
| 报告 | 总量/首包/请求/重复统计 |
| 预算 | 阈值红线 + CI 门禁防回退 |
| 监控 | Web Vitals + RUM 采集 |
| 闭环 | 测量→定位→优化→验证→门禁 |
| 长尾 | 大×低频模块按需化 |
| 慢构建 | transform 大头 + 缓存 |
| 治理 | 基线→预算→门禁→监控→制度 |
一句话记忆:包体分析与性能监控 = 把性能从「感觉」变成「数据 + 门禁」的制度化体系:分析用 rollup-plugin-visualizer 树图定位大模块(读法四看:最大方块/压缩比/首包分布/重复模块)+ 构建报告统计(总包/首包 = 入口+静态依赖链/请求数),大包体五大结论:误引入、未摇树、重复打包、未分割、多余依赖;性能预算设红线(首包 gzip<180KB/总包<800KB/构建<60s)由 CI 门禁拦截(check-budget 脚本超限 exit(1),集成 = 构建后检查 + 阻塞发布 + 看板趋势 + 例外豁免);运行时监控 = Web Vitals(LCP<2.5s/INP<200ms/CLS<0.1)+ web-vitals 采集上报 RUM + 资源/错误监控,决策看分位数 P75/P95 + 页面 + 设备 + 版本前后对比;加载优化闭环 = 测量基线 → Analyzer/瀑布定位 → 针对优化(首屏顺序:先砍首包体积分割摇树 → 优请求 preload/缓存/CDN → 调渲染关键 CSS/骨架屏 → 交互响应)→ 对比验证(前后 + A/B + Lighthouse/RUM)→ 预算门禁;长尾模块(大×低频×不该进首包)按需化四法:懒加载/子路径/按需库/拆包,按「频率×体积」平衡加载策略;慢构建定位 transform 大头(缓存+减插件+esbuild+并行)+ 依赖预打包缓存 + 压缩选型 + CI 共享缓存;治理四阶段(救火→度量→门禁→文化)落地节奏:基线 → 首包/LCP 预算 → CI 门禁 → RUM 监控 → 审查清单/负责人/SLA 制度化,失败四因:不执行、不看体验、过度优化、无归属。
延伸阅读
- /vite-build-optimization/ — 构建配置与产物优化
- /vite-runtime-performance-optimization/ — 运行时性能优化
- /vite-tree-shaking-deep-dive/ — Tree-shaking 与产物瘦身
- /vite-ci-cd-optimization/ — CI/CD 构建优化
- 前端工程专题 — 前端工程化与性能治理
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。