1. IntersectionObserver:懒加载的终极方案
// 创建一个观察器实例
// 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[data-src]').forEach(img => {
// 让观察器开始监听每个图片
observer.observe(img);
});
**优点:**首屏加载时间直接砍掉 40%,滚动丝滑,CPU 占用也降了。
关键是没有重排重绘,完全是浏览器层面的优化,比手动监听 scroll 强太多。
2. document.visibilityState:页面不可见时节省资源
用户切到别的标签页,页面还在疯狂发请求、跑动画?太浪费了,用 visibilityState 判断页面是否激活:页面不可见时暂停轮询、视频、动画,回来再恢复。省电、省流量、省服务器压力。
document.addEventListener('visibilitychange', () => {
// visibilityState 的值可能是:
// 'visible':页面在前台
// 'hidden':页面在后台(最小化、切标签)
if (document.visibilityState === 'hidden') {
// 页面不可见时,暂停视频播放
stopVideo();
// 停止定时轮询接口
stopPolling();
} else {
// 页面回到前台,恢复视频播放
resumeVideo();
}
});
3. Web Workers:把重任务移出主线程
项目里有个功能要处理上万条数据,一执行页面就卡死。后来用 Web Workers 把计算放到后台线程,主线程再也不卡了,用户可以正常操作页面,处理完再通知前端。
// 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);
};
4. Cache API + Service Worker:让页面离线可用
PWA 的核心就是缓存。用 Cache API,可以把静态资源存到客户端,,第一次访问正常加载,第二次直接从缓存读,速度快到飞起。
而且即使断网,核心页面也能打开,用户体验直接拉满。
// service-worker.js
self.addEventListener('fetch', event => {
// 拦截网络请求
event.respondWith(
// 先在缓存中查找是否有匹配的请求
caches.match(event.request).then(cached => {
// 如果缓存中有,直接返回缓存内容
// 否则发起网络请求
return cached || fetch(event.request);
})
);
});
5. performance.now():精准测量性能
Date.now() 精度不够,还可能被系统时间干扰。performance.now() 是高精度时间戳,适合测量函数执行时间:
// 获取当前高精度时间(毫秒,精确到微秒)
const start = performance.now();
// 执行一个耗时操作
heavyCalculation();
// 再次获取时间
const end = performance.now();
// 计算耗时,结果非常精确
console.log(`耗时: ${end - start}ms`);
6. ResizeObserver:监听元素尺寸变化
想监听某个 div 的宽高变化?别再用 window.resize + getBoundingClientRect 了,又慢又不准。ResizeObserver 可以精确监听任意元素的尺寸变化,特别适合图表、自适应容器这类组件,再也不用手动触发 resize 事件了。
const observer = new ResizeObserver(entries => {
// entries 包含所有被观察元素的尺寸信息
entries.forEach(entry => {
// entry.contentRect 包含元素的宽高、位置等
const { width, height } = entry.contentRect;
console.log(`元素尺寸:${width} x ${height}`);
// entry.target 是被观察的 DOM 元素
console.log('目标元素:', entry.target); // 可以在这里调整子元素布局或重绘图表
});
});
// 开始监听指定元素
observer.observe(document.getElementById('chart-container'));
// 停止观察
// 停止观察某个元素
observer .unobserve(element);
// 停止观察所有元素并释放资源
observer .disconnect();
7. requestAnimationFrame:动画就该这么写
以前用 setTimeout 做动画,总感觉卡卡的,还容易掉帧。换成 requestAnimationFrame 后,动画终于和屏幕刷新率同步了:
-
自动适配 60fps / 120fps 屏幕
-
页面不可见时自动暂停,省电
-
比
setTimeout更精准function animate() { // 更新元素位置 element.style.transform =
translateX(${x}px); // 如果还没到目标位置,继续下一帧动画 if (x < 200) { requestAnimationFrame(animate); } } // 启动动画 requestAnimationFrame(animate);
8. requestIdleCallback:把非关键任务丢到空闲时执行
有些事不着急,比如上报埋点、预加载下一页数据、清理缓存。但放在主线程里,总怕影响用户体验。requestIdleCallback 就是干这个的——告诉浏览器:“等你空了再执行”。它不会抢占主线程,适合处理低优先级任务。用了之后,页面交互明显更跟手了。
// 浏览器会在主线程空闲时执行这个回调
// 不会阻塞高优先级任务(如渲染、用户输入)
requestIdleCallback(() => {
// 发送用户行为埋点
sendAnalytics();
// 预加载下一页可能需要的资源
preloadNextPage();
});
9. Intl.NumberFormat —— 一键千分位
价格、股票、报表格式化
new Intl.NumberFormat('zh-CN').format(1234567); // 1,234,567
10. URLSearchParams —— 再也不用正则解析 query
路由、埋点、搜索参数
const params = new URLSearchParams(location.search);
params.get('id'); // ?id=123