事情本身不复杂,线索都在日志里。商品详情页的筛选按钮点下去要等两秒多才变色,CrUX 里 INP 分位数 2.4 秒,移动端更差。性能面板显示点击后的主线程被一次 JSON.parse 占了 1.9 秒。

第一反应是「缓存没有分片,单条记录就有 4MB」,验证下来不成立。

真正麻烦的地方在于,几个原因的现象几乎一样。筛选状态在每次点击时全量读出并解析整份缓存。还有一种是解析放在事件回调里同步执行,阻塞了渲染。

还有一种情况,点击回调里还触发了一次全量 DOM 重排 把耗时又翻了一倍。

第一件事是确认长任务落在哪个回调

performance.getEntriesByType('longtask').map(e => e.duration)

再往下把同步读取改成按需分片读取

const raw = localStorage.getItem('filters:' + group);

解析挪到 requestIdleCallback 里

requestIdleCallback(() => render(JSON.parse(raw)));

等前面都确认过,再用真实设备复测交互延迟

curl -s 'https://www.example.com/api/filters' -o /dev/null -w '%{time_total}\n'

恢复的判定其实很直接。改完后同一按钮的交互延迟降到 180 毫秒,CrUX 的 INP 分位数值在两周后的报表里落到 200 毫秒以内。

补一句。INP 看的是最差那一档的体验,平均值好看没有意义,压测时要用低端安卓机跑一遍。同一份配置在测试环境跑通,不代表线上没事,两边的默认值经常不一样。

作者 9823189