几十万个点位的散点图在 Canvas 2D 下卡到没法交互,换 WebGL 也只是把卡顿从主线程挪到了每帧几千次 draw call 上——记录我排查这个瓶颈、最终用 WebGPU 的 Compute Shader 重写数据聚合逻辑的过程。
Speculation Rules API 能让浏览器提前预取甚至预渲染下一跳页面,比 link rel=prefetch 和悬停预加载库更进一步,但预渲染错页面会白白浪费带宽,预渲染有副作用的页面还可能提前触发埋点或写操作,这些代价得先算清楚。
长任务把主线程堵住的时候,setTimeout(fn, 0) 和 requestIdleCallback 都救不了场——scheduler.postTask() 的优先级参数和 scheduler.yield() 的循环内让出,才是浏览器原生给出的任务调度方案。
页面不大、主线程也不忙,首屏却迟迟不出来——把 TTFB、FP、数据包、IP、TCP 握手这些网络层细节和前端性能连起来讲清楚。
Rolldown 把 Vite 的打包从 esbuild/Rollup 双引擎统一成一套 Rust 实现,记录我在中型项目里切过去的体感、兼容性问题和回退策略。
记录把 React Compiler 接入一个真实项目的过程:它替我做了哪些手动 memo 化、哪些写法会让它直接放弃优化、以及上线前怎么验证它没改坏行为。
Context 混放状态会让一次输入拖着半个页面重渲染——问题不在 Context 本身,而在状态该放在哪一层、归谁管,拆分职责、区分 state 和 actions 才是根本解法。
首屏指标再好看,用户点一下筛选还是能卡半拍——INP、长任务拆分、React 重渲染边界,才是交互性能真正要盯的地方。
批量请求不能只写一个 Promise.all;我更关心同时执行数量、失败重试、取消和用户等待时间怎么平衡。
页面慢不一定该上 Edge——渲染位置离用户近了,数据如果还在中心区域,那一跳只是换了个地方出现,缓存策略和运行时限制才是真正要先算清的账。
Service Worker 注册容易,但缓存策略想不清楚,用户会长期停留在旧版本、接口数据错乱——生命周期、不同资源的缓存策略、版本更新和断网时的退路该怎么分工。
React 组件为什么会被牵连着重新渲染:状态边界怎么划、memo 和 useCallback 什么时候真的有用、长列表和 Profiler 排查该按什么顺序来,而不是页面一卡就到处加缓存。
构建慢不一定是打包工具的锅:依赖体积、类型检查覆盖范围、缓存命中率、代码生成方式,任何一处出问题都会拖慢构建,换工具往往只是把慢延后了。
用户已经觉得页面慢了,开发手上却没有任何证据——Web Vitals 怎么采集才可靠、错误监控怎么带上下文、接口耗时和用户行为怎么串成一条能复现问题的时间线。
接口明明返回了新数据,页面却还是旧的——App Router 里叠着 Request Memoization、Data Cache、Full Route Cache、Router Cache 好几层,不分清楚各自的作用域和失效方式,只会越加 `no-store` 越乱。
首屏慢很多时候不是 JS 的问题,而是图片显示 360px 却下载了 2000px 的原图——LCP 图片怎么排查、响应式图片怎么配置、懒加载和占位尺寸各自该怎么取舍。
同一个页面卡顿,可能是 JavaScript 占住了主线程,也可能是布局计算太频繁,还可能是绘制区域太大——不搞清楚 DOM、CSSOM、布局、绘制、合成这几层各自在干什么,排查只能靠试。
React Hydration 为什么要把整个组件重新执行一遍,Qwik 的 Resumability 又是怎么绕开这次重复执行的:从两者的底层机制差异,拆到 mismatch 排查和第三方脚本对主线程的争用。
WebP 能把图片体积压下去,但这份收益要拿兼容回退、缓存协商和响应式尺寸的工程成本来换。这篇算清楚 WebP 到底省了多少、又要多担哪些事。
哪些图该懒加载、哪些绝不能懒,是这篇要讲清楚的判断标准:屏外的列表图该延后请求,首屏的 LCP 主图一旦被 loading=lazy 延后反而拖慢首屏。文中覆盖原生 lazy、fetchpriority、IntersectionObserver、占位尺寸与加载失败的补救。
包体积膨胀真正的成本不在压缩,而在分析、拆分和取舍:图表库富文本被打进首屏、公共 chunk 越抽越大、一行 import 引回大依赖,本文讲清楚这些代价怎么算、怎么用体积预算把它卡住。
页面卡顿时该先看什么:先把慢分成首屏、切换、滚动、输入四类,再用 Performance 面板、Web Vitals 和真实用户指标把资源、请求、主线程逐层验证,而不是先归因给框架。
Web Worker 该用在哪、结构化克隆和 Transferable 对象的性能差异、错误处理和任务取消要怎么设计,以及它解决不了什么问题——这篇把这次挪计算逻辑到 Worker 的判断过程理一遍。
强缓存和协商缓存到底谁优先、命中了会不会发请求,这些问题只看文档很容易记混。这篇拆清 Cache-Control、ETag、文件 hash、HTML 入口和接口缓存该怎么配,顺带讲清楚发布后用户还在看旧页面到底是哪一层出的问题。
SPA 挂一整天不刷新,内存从一百多 MB 涨到一个多 GB,页面卡到点不动——这类泄漏光看代码基本查不出来。整理这次用 DevTools 排查的完整路径:Performance 面板确认泄漏趋势、堆快照三连拍与 Comparison 视图的读法、Detached DOM 树、闭包持有和定时器未清理各自在快照里长什么样。
同样是往列表里塞一千条数据,直接循环 append 和先用 DocumentFragment 离线拼装再一次性插入,流畅度差得很明显。这篇讲清楚这个差异背后的重排重绘原理,以及和 innerHTML 拼接、事件委托、虚拟滚动这几个相邻方案的取舍。
骨架屏不是所有页面的标配:什么页面值得上骨架屏、什么页面转圈就够,判断标准比实现更重要。手写 CSS 骨架的实现细节、page-skeleton-webpack-plugin 这类构建期自动生成方案的取舍、SSR 场景的直出骨架、骨架和真实内容布局不一致导致的跳动怎么治,都在这次四个页面的落地里过了一遍。
HappyPack 已经停止维护,webpack 4 官方更推荐 thread-loader、cache-loader 这条路,但项目里的历史配置到底要不要换、什么时候该换,值得掰开讲清楚。
同样是判断'元素进入视口',scroll 事件加 getBoundingClientRect 和 IntersectionObserver 的差别不只是性能,语义上前者是'我不停地问'、后者是'浏览器主动告诉我'。整理列表曝光埋点和触底加载两个场景下 IO 的 threshold/rootMargin 用法、曝光去重与可见时长统计,以及 root、iframe 相关的几个坑。
服务端已经开了 HTTP/2,瀑布图里资源却还在一批一批地排队——问题出在静态资源域名协议没跟上。文章梳理多路复用的原理,以及域名分片、雪碧图这类 HTTP/1.1 时代的优化习惯在 HTTP/2 下该怎么重新算账。
页面滚动掉帧,不一定是 JavaScript 算慢了。把 DOM、CSSOM、渲染树、重排、重绘和合成层放回一次真实排查里,看瓶颈到底出在哪一段。
首屏慢很多时候不是内容太多,而是把不需要立刻出现的重组件也塞进了主包。这里从一个富文本编辑器拖慢详情页开始,把异步组件和懒加载判断标准理清。
CDN 切完后只有一个省的用户集体白屏,问题不在 JS,而在解析链路的缓存层层叠加——DNS、TTL、CNAME 这几层任何一层没跟上都会留下旧结果。文章梳理 DNS 解析链路、CDN 调度原理和多地访问差异背后的排查思路。
搜索联想、滚动监听、按钮防重复点看起来都像“别触发太频繁”,真正要分清的却是防抖、节流和状态锁各自在挡什么。这里把概念、场景和边界重新理顺。
首屏包太大时,最怕的是人人都在猜。这里从一张体积分布图出发,把 webpack-bundle-analyzer 怎么定位重模块、重复依赖和错误拆分讲清楚。
同一份代码强刷是新的,普通刷新却还是旧页面。把强缓存、协商缓存、304 和前端发版策略放回同一个场景里,讲清浏览器为什么会这样表现。