用这 9 个 API,我把页面性能干到了 90+
<h1 data-id="heading-0">🧑💻 写在开头</h1><p>点赞 + 收藏 === 学会🤣🤣🤣</p>
<div>
<div>
<blockquote>
<p>最近项目上线,用户一多,页面就卡得不行。首屏加载 3 秒起,滚动掉帧,手机发烫……被 QA 喊去聊了好几次。</p>
<p>没办法,只能低头研究性能优化。翻了一圈文档和实战案例,发现现代浏览器其实给了我们很多“外挂”——那些你可能听过但一直没用起来的高级 API。</p>
<p>真正用上去之后,页面流畅度提升非常明显。今天就来分享我在项目中实测有效的 9 个 API,每一个都带来了实实在在的性能提升。</p>
</blockquote>
</div>
<h2 data-id="heading-0">1. <code>IntersectionObserver</code>:懒加载的终极方案</h2>
<p>以前做图片懒加载,都是监听 <code>scroll</code> 事件,手动判断元素位置。结果就是:一滚动,页面卡成 PPT。</p>
<p>后来改用 <code>IntersectionObserver</code>,直接交给浏览器去监听:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// 创建一个观察器实例
// entries 是所有被观察元素的状态集合
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
// 判断元素是否进入视口(可见)
if (entry.isIntersecting) {
const img = entry.target; // 获取当前图片元素
// 将 data-src 中的真实图片地址赋给 src,开始加载
img.src = img.dataset.src;
// 加载完成后,停止观察,避免重复触发
observer.unobserve(img);
}
});
});
// 找到所有带有 data-src 的图片(懒加载图片)
document.querySelectorAll('img').forEach(img => {
// 让观察器开始监听每个图片
observer.observe(img);
});</pre>
</div>
<div>
<div>
<p><strong>效果</strong>:首屏加载时间直接砍掉 40%,滚动丝滑,CPU 占用也降了。</p>
<p>关键是没有重排重绘,完全是浏览器层面的优化,比手动监听 <code>scroll</code> 强太多。</p>
<hr>
<h2 data-id="heading-1">2. <code>requestIdleCallback</code>:把非关键任务丢到空闲时执行</h2>
<p>有些事不着急,比如上报埋点、预加载下一页数据、清理缓存。但放在主线程里,总怕影响用户体验。</p>
<p><code>requestIdleCallback</code> 就是干这个的——告诉浏览器:“等你空了再执行”。</p>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// 浏览器会在主线程空闲时执行这个回调
// 不会阻塞高优先级任务(如渲染、用户输入)
requestIdleCallback(() => {
// 发送用户行为埋点
sendAnalytics();
// 预加载下一页可能需要的资源
preloadNextPage();
});</pre>
</div>
<div>
<div>
<p>它不会抢占主线程,适合处理低优先级任务。用了之后,页面交互明显更跟手了。</p>
<hr>
<h2 data-id="heading-2">3. <code>requestAnimationFrame</code>:动画就该这么写</h2>
<p>以前用 <code>setTimeout</code> 做动画,总感觉卡卡的,还容易掉帧。</p>
<p>换成 <code>requestAnimationFrame</code> 后,动画终于和屏幕刷新率同步了:</p>
</div>
<br>
<div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">function animate() {
// 更新元素位置
element.style.transform = `translateX(${x}px)`;
// 如果还没到目标位置,继续下一帧动画
if (x < 200) {
requestAnimationFrame(animate);
}
}
// 启动动画
requestAnimationFrame(animate);</pre>
</div>
<div>
<div>
<p><strong>优势</strong>:</p>
<ul>
<li>自动适配 60fps / 120fps 屏幕</li>
<li>页面不可见时自动暂停,省电</li>
<li>比 <code>setTimeout</code> 更精准</li>
</ul>
<p>动画类交互都建议换成这个。</p>
<hr>
<h2 data-id="heading-3">4. <code>ResizeObserver</code>:监听元素尺寸变化</h2>
<p>想监听某个 div 的宽高变化?别再用 <code>window.resize</code> + <code>getBoundingClientRect</code> 了,又慢又不准。</p>
<p><code>ResizeObserver</code> 可以精确监听任意元素的尺寸变化:</p>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">const observer = new ResizeObserver(entries => {
// entries 包含所有被观察元素的尺寸信息
entries.forEach(entry => {
// entry.contentRect 包含元素的宽高、位置等
console.log('新尺寸:', entry.contentRect);
// 可以在这里调整子元素布局或重绘图表
});
});
// 开始监听指定元素
observer.observe(document.getElementById('chart-container'));</pre>
</div>
<div>
<div>
<p>特别适合图表、自适应容器这类组件,再也不用手动触发 resize 事件了。</p>
<hr>
<h2 data-id="heading-4">5. <code>performance.now()</code>:精准测量性能</h2>
<p><code>Date.now()</code> 精度不够,还可能被系统时间干扰。</p>
<p><code>performance.now()</code> 是高精度时间戳,适合测量函数执行时间:</p>
</div>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// 获取当前高精度时间(毫秒,精确到微秒)
const start = performance.now();
// 执行一个耗时操作
heavyCalculation();
// 再次获取时间
const end = performance.now();
// 计算耗时,结果非常精确
console.log(`耗时: ${end - start}ms`);</pre>
</div>
<p>配合 <code>performance.mark()</code> 和 <code>measure()</code>,还能做更复杂的性能分析。</p>
<hr>
<h2 data-id="heading-5">6. <code>preload</code> 和 <code>prefetch</code>:资源预加载</h2>
<h3 data-id="heading-6"><code>preload</code>:关键资源,立刻加载</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;"><!-- 告诉浏览器:这个 CSS 很重要,马上就要用,优先加载 -->
<link rel="preload" href="critical.css" as="style">
<!-- 预加载字体文件,避免文字闪动 -->
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin></pre>
</div>
<p>用于首屏必须用到的资源,浏览器会优先加载,提升首屏速度。</p>
<h3 data-id="heading-7"><code>prefetch</code>:未来可能用到的资源</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;"><!-- 告诉浏览器:用户可能会访问下一页,空闲时预加载这个 JS -->
<link rel="prefetch" href="/user/profile.js"></pre>
</div>
<p>在空闲时预加载下一页的 JS 或数据,实现“秒开”跳转。</p>
<p>这两个配合使用,体验提升非常明显。</p>
<hr>
<h2 data-id="heading-8">7. <code>Cache API</code> + <code>Service Worker</code>:让页面离线可用</h2>
<p>PWA 的核心就是缓存。用 <code>Cache API</code>,可以把静态资源存到客户端:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// service-worker.js
self.addEventListener('fetch', event => {
// 拦截网络请求
event.respondWith(
// 先在缓存中查找是否有匹配的请求
caches.match(event.request).then(cached => {
// 如果缓存中有,直接返回缓存内容
// 否则发起网络请求
return cached || fetch(event.request);
})
);
});</pre>
</div>
<div>
<div>
<p>第一次访问正常加载,第二次直接从缓存读,速度快到飞起。</p>
<p>而且即使断网,核心页面也能打开,用户体验直接拉满。</p>
<hr>
<h2 data-id="heading-9">8. <code>Web Workers</code>:把重任务移出主线程</h2>
<p>项目里有个功能要处理上万条数据,一执行页面就卡死。</p>
<p>后来用 <code>Web Workers</code> 把计算放到后台线程:</p>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// main.js - 主线程
// 创建一个 Web Worker,运行 worker.js 文件
const worker = new Worker('worker.js');
// 发送数据给 Worker
worker.postMessage(data);
// 监听 Worker 的返回结果
worker.onmessage = (e) => {
console.log('处理完成:', e.data);
};
// worker.js - 后台线程
// 监听来自主线程的消息
self.onmessage = function(e) {
// 执行耗时的数据处理
const result = heavyProcess(e.data);
// 将结果返回给主线程
self.postMessage(result);
};</pre>
</div>
<p>主线程再也不卡了,用户可以正常操作页面,处理完再通知前端。</p>
<hr>
<h2 data-id="heading-10">9. <code>document.visibilityState</code>:页面不可见时节省资源</h2>
<p>用户切到别的标签页,页面还在疯狂发请求、跑动画?太浪费了。</p>
<p>用 <code>visibilityState</code> 判断页面是否激活:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">document.addEventListener('visibilitychange', () => {
// visibilityState 的值可能是:
// 'visible':页面在前台
// 'hidden':页面在后台(最小化、切标签)
if (document.visibilityState === 'hidden') {
// 页面不可见时,暂停视频播放
stopVideo();
// 停止定时轮询接口
stopPolling();
} else {
// 页面回到前台,恢复视频播放
resumeVideo();
}
});</pre>
</div>
<div>
<div>
<p>页面不可见时暂停轮询、视频、动画,回来再恢复。省电、省流量、省服务器压力。</p>
<hr>
<h2 data-id="heading-11">总结</h2>
<p>这 9 个 API 不是“炫技”,而是真正在解决性能问题:</p>
<ul>
<li><code>IntersectionObserver</code> → 懒加载</li>
<li><code>requestIdleCallback</code> → 空闲任务</li>
<li><code>requestAnimationFrame</code> → 流畅动画</li>
<li><code>ResizeObserver</code> → 尺寸监听</li>
<li><code>performance.now()</code> → 性能测量</li>
<li><code>preload/prefetch</code> → 资源预加载</li>
<li><code>Cache API</code> → 离线缓存</li>
<li><code>Web Workers</code> → 后台计算</li>
<li><code>visibilityState</code> → 节流优化</li>
</ul>
<p>每一个都能在特定场景下带来显著提升。建议从 <code>IntersectionObserver</code> 和 <code>preload</code> 开始,逐步引入,效果立竿见影。</p>
</div>
<div>
<h3 id="tid-D8HBxE">如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。</h3>
</div>
<p><em><img src="https://img2024.cnblogs.com/blog/2149129/202501/2149129-20250122165814748-630765389.png" alt="" loading="lazy"></em></p>
</div>
</div>
</div>
</div>
</div>
</div>
</div><br><br>
来源:https://www.cnblogs.com/smileZAZ/p/19507379
頁:
[1]