性能优化 2026 Q1

719 字 · 2 分钟#性能#工程化

这篇是一月中旬写的阶段记录,写的时候优化只推进完前半部分,后面几个方向刚列了标题还没动手。之后一直没顾上补,索性按当时的样子放出来,做了多少写多少。

这一轮性能优化的重点不是运行时(变更检测、渲染效率这些),而是构建产物:控制 bundle 体积、调整 chunk 拆分,减少首屏要加载的资源,把非关键代码拆到异步 chunk 里。

用 stats.json 定位体积问题

优化的第一步永远是定位问题。项目是 Angular,构建走 esbuild,可以生成 stats.json 来分析打包结果:

  • 命令行:ng build --stats-json
  • 或在构建配置里开 statsJson: true(见 官方文档

生成之后上传到 esbuild.github.io/analyze,treemap 里各依赖的体积占比一目了然。

动手前还改造了一下构建命令:原来的流程没法向构建透传自定义参数,改造后支持 pnpm run build --stats-json,另外加了一个统一入口,一次性构建全部五个应用——先单独构建最大的主应用,其余四个分两批并行,全程自动带上 --stats-json,产物都能拿来做体积对比。

(分析截图里全是公司内部的包名,就不贴了。)

统一包版本,避免重复打包

产物里出现同一个依赖的多个版本,体积白白翻倍,还可能引发运行时差异。analyze 页面提示「This bundle has n warnings」时,多半就是重复版本。

用 pnpm overrides 把依赖和传递依赖锁到同一个版本:

{
  "pnpm": {
    "overrides": {
      "d3-color": "3.0.1"
    }
  }
}

这一项每个应用平均减小约 0.1 MB,五个应用累计省 0.5 MB。

优化自研包的打包方式

内部有一个公式计算包,是体积重灾区。在不改功能的前提下调整打包方式、剔除冗余代码和依赖,main.js 从 1.73 MB 压到 82.97 KB,降了约 95%,gzip 后只有 19.9 KB。

每个应用平均减小约 0.4 MB,累计省 2 MB。后续还有空间:CJS 转 ESM、启用 tree-shaking,把面向对象的部分改成函数式实现。

按需引入,让 tree-shaking 真正生效

典型案例是 highlight.js。原来 import hljs from 'highlight.js' 全量引入,所有语言模块一起被打包。但富文本编辑器实际只需要十几种语言,改成引核心模块加手动注册:

import hljs from 'highlight.js/lib/core'
import bash from 'highlight.js/lib/languages/bash'
import javascript from 'highlight.js/lib/languages/javascript'
import python from 'highlight.js/lib/languages/python'
import sql from 'highlight.js/lib/languages/sql'
// ...实际用到的十几种语言

hljs.registerLanguage('bash', bash)
hljs.registerLanguage('javascript', javascript)
hljs.registerLanguage('python', python)
hljs.registerLanguage('sql', sql)
// ...

这个依赖从约 1 MB 压到 78 KB,降了约 93%。每个应用平均减小约 0.9 MB,累计省 5 MB。

还没做的

写到这里的时候,下面几个方向只列了计划:

  • 剔除不必要的依赖、字体和 CSS
  • 把 CJS 包转成 ESM,让 tree-shaking 能覆盖到
  • chunk 拆分:懒加载和动态导入降首屏体积,非关键模块合理拆出去

做完再补。