JavaScript的秘密生活:观察者
Source: Dev.to
Timothy 靠在椅子上,听着笔记本风扇突然的、刺耳的嗡鸣。他刚刚为一个巨大的用户头像网格实现了懒加载功能。
“滚动非常流畅,” Timothy 边说边轻点屏幕。“我用了我们昨天讨论过的
{ passive: true }标记。合成线程完全没有被阻塞。但我的 CPU 使用率瞬间飙到 90%,笔记本的风扇好像要起飞了。”
Margaret 手里端着咖啡走了过来,瞄了一眼他副屏上的性能监视器。
“你成功解放了列车,” Margaret 点头看着屏幕说。“但你在折磨调度员。”
滚动轮询的问题
负责懒加载的代码直接绑定在 scroll 事件上:
const images = document.querySelectorAll('img[data-src]');
window.addEventListener('scroll', () => {
images.forEach(img => {
// Calculate exact geometry on every scroll tick
const rect = img.getBoundingClientRect();
// If the image enters the viewport, load it
if (rect.top {
entries.forEach(entry => {
// 2. Callback when the element intersects
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src; // Load the image
// 3. Stop observing once the image has loaded
observer.unobserve(img);
}
});
}, options);
// 4. Register all images with the observer
images.forEach(img => observer.observe(img));
浏览器现在在其优化的 C++ 引擎中完成所有空间计算。只有在真正需要加载图片时,JavaScript 才会被唤醒,并立即取消观察,以避免重复回调。
好处与结论
- 降低 CPU 使用率 – 不再在每一次滚动时进行持续的布局计算。
- 滚动更顺畅 – 主线程保持空闲,可用于渲染和用户交互。
- 代码更简洁 – 观察者模式将加载逻辑封装在单一、作用域明确的回调中。
切换到 IntersectionObserver 后,Timothy 刷新页面并在巨大的网格中滚动。滚动依旧丝般顺滑,图片在进入视口前就已出现,笔记本风扇也显著安静下来。