1.requestIdleCallback
浏览器提供的 API,用于在 浏览器空闲时执行低优先级任务,不会阻塞主线程,也不会影响动画和用户交互。
使用场景
- 非紧急任务(如日志上报,数据同步,预加载等)
- 避免影响UI渲染卡顿
const idleCallbackId = requestIdleCallback(callback, options);
// 取消回调
cancelIdleCallback(idleCallbackId);
const bigData = Array.from({ length: 10000 }, (_, i) => `Item${i + 1}`)
function processDataInBatches(idleDeadline) {
// 每次最多处理100条或者直到空闲时间用完
if (bigData.length > 0 && (idleDeadline.timeRemaining() > 0 || idleDeadline.didTimeout)) {
const item = bigData.pop()
console.log('Processing:', item)
}
if (bigData.length > 0) {
requestIdleCallback(processDataInBatches, {
timeout: 1000,
})
} else {
console.log('✅ All data processed!')
}
}
// 启动处理(设置超时1000ms,强制执行回调,防止迟迟不执行)
requestIdleCallback(processDataInBatches, { timeout: 1000 })
2.IntersectionObserver
浏览器提供API, 用于异步观察一个元素与其祖先元素或顶级文档视口viewport之间的交集变化, 简言之, 其可以高效监听某个元素是否进入或离开视口, 以及可见部分的比例.
优势
- 高性能:浏览器原生实现,比传统的滚动事件监听+getBoundingClientRect()计算更高效
- 异步执行:回调在空闲时期触发,不阻塞主线程
- 精确控制:可以设置交叉比例阈值和根元素
- 当交叉比例(intersection ratio)达到指定阈值时,会触发回调函数。
使用场景
- 懒加载, 当组件或图片进入视口时加载, 提高性能
- 无限滚动, 监测滚动到底部, 自动加载内容
- 广告曝光统计, 检测广告是否被用户看到,以进行数据分析
懒加载:
<img class="lazy" data-src="real-image.jpg" src="placeholder.jpg" alt="示例图片">
document.addEventListener("DOMContentLoaded", () => {
const lazyImages = document.querySelectorAll('.lazy');
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('lazy');
observer.unobserve(img); // 加载后停止观察
}
});
}, {
rootMargin: '100px' // 提前100px加载
});
lazyImages.forEach(image => {
imageObserver.observe(image);
});
});
无限滚动:
const sentinel = document.querySelector('#sentinel');
let isLoading = false;
const infiniteScrollObserver = new IntersectionObserver(async (entries) => {
const entry = entries[0];
if (entry.isIntersecting && !isLoading) {
isLoading = true;
try {
await loadMoreItems(); // 你的数据加载函数
} finally {
isLoading = false;
}
}
});
infiniteScrollObserver.observe(sentinel);
3.WeakMap
// 传统方式(内存泄漏风险):
const domDataMap = new Map();
domDataMap.set(element, { count: 0 });
// 优化方案:
const weakMap = new WeakMap();
weakMap.set(element, { count: 0 }); // 当element被移除D0M时,关联数据自动GC
4.ResizeObserver
ResizeObserver 是 原生 JavaScript API,用于监听 DOM 元素尺寸的变化,并在尺寸变化时执行回调函数。它可以检测元素的 width 和 height 变化,而 不需要监听 window.resize 事件。
// 传统响应式布局
window.addEventListener(
"resize",
debounce(() => {
const width = container.offsetWidth;
adjustElements(width);
}, 200),
);
// 优化方案
const resizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
console.log('Element:', entry.target);
console.log('Content Rect:', entry.contentRect);
console.log('Width:', entry.contentRect.width);
console.log('Height:', entry.contentRect.height);
}
});
const element = document.querySelector('#myElement');
resizeObserver.observe(element);
5.Web Worker
浏览器提供的多线程技术,用于 在后台运行 JavaScript 代码,避免主线程阻塞,提高应用的 性能和响应速度。
主线程与worker线程通过postMessage/onmessage进行通信
// 主线程
const imageWorker = new Worker('image-worker.js');
// 发送图像数据到Worker
imageWorker.postMessage({
cmd: 'applyFilter',
imageData: imageData,
filterType: 'grayscale'
});
// 接收Worker处理结果
imageWorker.onmessage = (e) => {
// ......
};
// 错误处理
imageWorker.onerror = (error) => {
// ......
};
// image-worker.js
// 监听主线程消息
self.onmessage = async (e) => {
const { cmd, imageData, filterType } = e.data;
if (cmd === 'applyFilter') {
let result;
switch (filterType) {
case 'grayscale':
result = await applyGrayscale(imageData);
break;
// 可扩展其他滤镜...
}
// 返回处理结果
self.postMessage(result);
}
};
6.requestAnimationFrame
浏览器提供的 高性能动画 API,用于在 下一帧渲染时执行回调函数,使动画更加 流畅,并且 降低 CPU/GPU 资源消耗。
function animate() {
// 动画逻辑......
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
7.URL.createObjectURL
浏览器提供的 API,用于创建 Blob 或 File 对象的临时URL,可以用来 本地预览文件(如图片、视频)、下载文件,而不需要上传到服务器, 需要手动释放内存(URL.revokeObjectURL())
// 导出Blob数据为可下载链接
const blob = new Blob([JSON.stringify(data)], {type: 'application/json'});
const url = URL.createObjectURL(blob); // 直接复制给 img.src / a.href 用于预览或下载
const a = document.createElement('a');
a.href = url;
a.download = 'data.json';
a.click();
URL.revokeObjectURL(url);
8.MutationObserver
异步监测DOM树变化的能力,可以高效地观察特定DOM节点的添加、移除或属性修改
<!-- 基础HTML结构 -->
<div id="observable-container">
<div class="item">初始项目</div>
</div>
<button id="add-btn">添加项目</button>
<button id="change-btn">修改属性</button>
<script>
const container = document.getElementById('observable-container');
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
console.log('检测到变化类型:', mutation.type);
if (mutation.addedNodes.length > 0) {
console.log('新增节点:', mutation.addedNodes);
}
if (mutation.attributeName) {
console.log(`属性 ${mutation.attributeName} 被修改`,
mutation.target.getAttribute(mutation.attributeName));
}
});
});
// 配置观察选项
const config = {
childList: true, // 观察子节点变化
attributes: true, // 观察属性变化
subtree: true, // 观察所有后代节点
attributeOldValue: true, // 记录旧属性值
attributeFilter: ['class', 'data-status'] // 只观察特定属性
};
// 开始观察
observer.observe(container, config);
// 测试操作
document.getElementById('add-btn').addEventListener('click', () => {
const newItem = document.createElement('div');
newItem.className = 'item';
newItem.textContent = `新项目 ${new Date().toLocaleTimeString()}`;
container.appendChild(newItem);
});
document.getElementById('change-btn').addEventListener('click', () => {
container.firstElementChild.classList.toggle('highlight');
container.setAttribute('data-status', 'modified');
});
// 停止观察
// observer.disconnect();
</script>
9. Performance.now() 精准测量性能
// 获取当前高精度时间(毫秒,精确到微秒)
const start = performance.now();
// 执行一个耗时操作
doSomething();
// 再次获取时间
const end = performance.now();
// 计算耗时,结果非常精确
console.log(`耗时: ${end - start}ms`);
10. preload & prefetch 资源预加载
| 特性 | preload | prefetch |
|---|---|---|
| 加载时机 | 当前页面 必需资源,高优先级 | 下个页面 可能资源,低优先级 |
| 适用场景 | 关键资源(字体、首屏CSS/JS) | 预测用户下一步需要的资源(如下一页JS) |
| 缓存行为 | 存入 HTTP 缓存,立即执行 | 存入 HTTP 缓存,按需执行 |
preload: 强制浏览器立即高优先级加载当前页面必需的资源,避免渲染阻塞。
<!-- 关键字体 -->
<link rel="preload" href="font.woff2" as="font" crossorigin>
<!-- 首屏CSS -->
<link rel="preload" href="critical.css" as="style">
<!-- 首屏JS -->
<link rel="preload" href="main.js" as="script">
prefetch: 空闲时低优先级加载后续页面可能需要的资源,提升后续导航体验。
<!-- 预加载下一个页面的JS -->
<link rel="prefetch" href="next-page.js" as="script">
<!-- 预加载图片 -->
<link rel="prefetch" href="hero-image.jpg" as="image">
11. Cache API + Service Worker 离线可用
PWA核心就是缓存, 将静态资源存到客户端, 首次访问正常加载, 二次访问从缓存读取, 离线也能访问.
// service-worker.js
self.addEventListener('fetch', event => {
// 拦截网络请求
event.respondWith(
// 先在缓存中查找是否有匹配的请求
caches.match(event.request).then(cached => {
// 如果缓存中有,直接返回缓存内容
// 否则发起网络请求
return cached || fetch(event.request);
})
);
});
12. Web Worker 开启子线程处理重任务
处理大数据量, 导致页面卡死. 通过 Web Worker 将任务放在后台线程执行, 处理完毕后再通知主线程, 保证页面不卡顿.
// 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);
};
13. document.visibilityState页面不可见时节省资源
切到别的标签页,页面还在疯狂发请求、跑动画?
document.addEventListener('visibilitychange', () => {
// visibilityState 的值可能是:
// 'visible':页面在前台
// 'hidden':页面在后台(最小化、切标签)
if (document.visibilityState === 'hidden') {
// 停止定时轮询接口
stopPolling();
} else {
// 页面回到前台,恢复操作
reStartPolling();
}
});