1. 先看清问题:性能优化不是拍脑袋,先建立量化指标
做“大前端通用性能优化”,最难的不是不会用工具,而是不知道到底该优化什么。很多人接到一个卡顿项目,第一反应是打开 Chrome DevTools 的 Performance 面板录一段性能 Profile,看到满屏红色长条就开始慌——到底哪里是瓶颈,哪里只是看起来慢,没有量化数据支撑,基本都是盲人摸象。
这里我建议先做两件事:建指标、跑基线。所谓建指标,就是选定几个能真实反映用户体验的数字,比如首次内容绘制(FCP)、最大内容绘制(LCP)、首次输入延迟(INP/TBT)、累计布局偏移(CLS),以及长任务(Long Task)的数量和总阻塞时间(TBT)。可以在用户真实设备上通过 PerformanceObserver 采集,也可以先在本地用 Lighthouse 跑一轮模拟数据。重点是让团队所有人对“慢”达成共识,避免你说“感觉卡”,他说“我这里很快”。
跑基线就更直接了。优化前先给项目拍个“体检片”,不管是大屏可视化项目、小程序还是 React Native 应用,至少在三个典型场景下跑一遍:首页冷启动、列表长滚动、图片密集页面。每个场景记录关键指标、资源体积、请求数量。基线数据一出来,优化的优先级就会自动浮出水面——如果图片占了页面总资源体积的 70%,那先处理图片;如果 Long Task 集中在某个第三方脚本,那就优先拆它。
1.1 性能指标到底看哪些,怎么理解才有用
很多人一听到核心 Web 指标就头大,觉得是 Chrome 团队拿来评分的,跟真实体验关系不大。其实换个角度就通了:FCP 对应“页面是不是白屏了”,LCP 对应“最重要的内容出来没有”,INP 对应“我点了按钮反馈快不快”,CLS 对应“页面是不是乱跳”。这四个指标分别覆盖加载、渲染、交互、视觉稳定,组合起来就是一套用户视角的体验模型。
做专项优化时,我会更关注 TBT 和 Long Task,这两个指标直接反映主线程是否被长时间占用。举一个我测过的真实案例:一个管理后台列表页,用户反馈筛选时能明显感到“一卡一顿”。Performance 录制数据显示,主线程上有一个长达 820ms 的长任务,原因是某次筛选后要对三万行数据进行汇总计算,并把结果一次性 setState 到表格组件里。这个场景里 FCP 和 LCP 都正常,唯独 TBT 爆红。如果光盯着首屏那点时间,这个问题永远发现不了。
还有一点必须提醒:不要只看本地模拟数据。低端 Android 手机、弱网环境、老版本 WebView,跑出来的指标和 Mac 上 Chrome 完全是两个世界。有条件就接一点真实用户监控(RUM),至少也要在 DevTools 里打开 CPU 6x 降速和网络 Fast 3G 再测一遍。只有这样你才知道那些“本地不卡”的页面,到底是怎么把用户卡到骂娘的。
1.2 用浏览器工具快速定位瓶颈的套路
最快的切入方式是打开 Chrome DevTools 的 Performance 面板,按一次录制,然后做一次目标操作(比如刷新页面、滚动列表、点击按钮)。录完第一件事不是看火焰图,而是看底部 Summary 里的脚本(Scripting)、渲染(Rendering)、绘制(Painting)占比。如果 Scripting 高,说明是 JS 和数据处理问题;如果 Rendering/Painting 高,说明 DOM 布局和重绘也有压力。
把火焰图按“Self Time”排序,能直接找出占用主线程最大的函数。这里有个实战经验:遇到压缩后的代码(比如生产环境)时,火焰图里全是无意义的单字母变量和压缩后的 chunk,根本没法看。所以排查问题最好先在本地开发环境跑,或者把 sourcemap 单独部署到可访问的地址,让浏览器能够还原源码。如果已经上了生产又没保留 sourcemap,可以临时用自定义函数命名保留参数的方式重新构建一次用来诊断,但千万不要把 sourcemap 泄露到公网,容易被人把业务代码看得底朝天。
Lighthouse 适合整体体检,但它跑的是固定页面的模拟流程,很多交互场景覆盖不到。我通常的套路是:先用 Lighthouse 拿总体得分和性能问题的粗线索,再用 Performance 面板深入函数级分析,最后结合 Network 面板按请求耗时排序,看是接口慢、资源加载慢还是执行慢。三个工具互相印证,基本能覆盖 80% 的前端性能问题定位需求。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 高频场景一:首屏渲染与白屏时间压榨
首屏是用户对一个产品的第一印象,也是大前端优化里最高频的专项。不管是 Web H5、小程序还是跨端应用,用户点击进入的第一秒体验,直接决定跳出率。首屏性能被拆开来看就两部分:加载资源和执行渲染。资源太大、请求太多、依赖阻塞、渲染时机不对,都会让白屏时间被拉长。
我在做
