直奔主题,如果页面中有100万个任务需要执行,怎么保证页面不卡顿?
可以采取以下几种策略:
-
任务分片执行:
- 利用
requestIdleCallback
和requestAnimationFrame
来分片执行任务。requestIdleCallback
可以在浏览器空闲时执行任务,而requestAnimationFrame
则可以在每次重绘前执行一个任务,这样可以保证在不影响页面渲染的情况下执行任务。
- 利用
-
Web Worker多线程处理:
- 使用Web Worker可以在独立线程中执行JavaScript代码,避免主线程阻塞,从而提高页面响应速度。
-
懒加载技术(Lazy Loading):
- 对于不需要立即执行的任务,可以采用懒加载技术,即在需要时才加载和执行,减少初始加载的压力。
-
优化DOM操作:
- 减少DOM操作和重绘重排,例如通过缓存DOM属性值来减少回流和重绘。
-
简化DOM结构:
- 简化DOM结构可以减少渲染和操作的复杂度,提高性能。
-
函数式组件和变量本地化:
- 在Vue中,使用函数式组件可以减少渲染开销,同时将多次引用的变量保存起来,减少响应式对象的getter触发次数,提高性能。
-
子组件分割:
- 将耗时任务分割到子组件中,利用Vue的组件粒度更新机制,避免不必要的渲染和计算。
-
第三方插件按需引入:
- 按需引入第三方组件库,避免体积过大,减少不必要的加载和执行。
-
路由懒加载:
- 在单页应用中,使用路由懒加载可以减少首页加载时需要加载的内容,加快加载速度。
-
监控和定位卡顿:
- 使用Performance工具监控页面性能,定位耗时任务,并进行优化。
方案1:任务分片通常是指将一个大任务分解成多个小任务,并在浏览器的空闲时间里逐一执行这些小任务,以此来避免长时间的阻塞主线程。下面是一个使用requestIdleCallback
实现任务分片的示例代码。这个例子中,我们将模拟一个需要处理100万个数字并将它们相加的任务。
// 模拟一个包含100万个数字的数组
const numbers = new Array(1000000).fill(1).map((x, i) => i + 1);// 用于累加的变量
let sum = 0;// 任务分片执行的函数
function processNumbers(start, end, deadline) {// 检查是否还有剩余任务if (start < end) {// 计算当前空闲时间可以处理的任务数量const chunk = Math.min((end - start) / 2, 10000); // 每次处理10000个数字for (let i = start; i < start + chunk; i++) {sum += numbers[i];}// 递归调用,处理下一批任务requestIdleCallback((deadline) => {processNumbers(start + chunk, Math.min(start + chunk * 2, end), deadline);});}
}// 开始执行任务分片
requestIdleCallback((deadline) => {processNumbers(0, numbers.length, deadline);
});window.addEventListener('load', () => {console.log(`The sum of numbers is: ${sum}`);
});
这段代码首先创建了一个包含100万个数字的数组,然后定义了一个processNumbers
函数,该函数接受三个参数:起始索引start
、结束索引end
和deadline
对象。deadline
对象包含了timeRemaining()
方法,该方法返回浏览器在当前帧剩余的时间,单位为毫秒。
在processNumbers
函数中,计算出当前空闲时间可以处理的任务数量,并在数组的指定范围内进行累加操作。然后,我们递归地调用requestIdleCallback
来处理下一批任务,直到所有任务都被处理完毕。
最后,在页面加载完成后在控制台输出累加的结果。
注意,这个代码只是一个示例,实际应用中可能需要根据具体任务进行调整。此外,由于requestIdleCallback
是异步的,所以最终的累加结果sum
可能不会立即可用,需要在所有任务完成后才能获取。
方案2:使用Web Worker可以实现多线程处理任务,这样可以避免主线程阻塞。下面是一个使用Web Worker处理100万个数字求和的示例代码。
主线程代码(HTML文件)
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><script>const worker = new Worker("worker.js");//监听worker消息worker.onmessage = function (e) {console.log(e.data);};//启动worker任务worker.postMessage({type: "start",numbers: new Array(1000000).fill(1).map((x, i) => i + 1),});</script></body>
</html>
Web Worker代码(worker.js)
//接收主线程消息
self.onmessage = function (e) {if (e.data.type === "start") {const numbers = e.data.numbers;const sum = calculateSum(numbers);postMessage(sum);}
};
//计算数组中所有数字的和
function calculateSum(numbers) {return numbers.reduce((acc, curr) => acc + curr, 0);
}
说明
-
主线程代码:
- 创建一个Web Worker实例,指向
worker.js
文件。 - 监听Web Worker的消息,当收到消息时,在控制台输出结果。
- 向Web Worker发送一个包含100万个数字的数组,并启动求和任务。
- 创建一个Web Worker实例,指向
-
Web Worker代码:
- 监听主线程发送的消息,当收到类型为
start
的消息时,开始处理任务。 - 定义一个
calculateSum
函数,使用reduce
方法计算数组中所有数字的和。 - 将计算结果通过
postMessage
发送回主线程。
- 监听主线程发送的消息,当收到类型为
运行步骤
- 将主线程代码保存为
index.html
文件。 - 将Web Worker代码保存为
worker.js
文件,并确保与index.html
文件在同一目录下。 - 在浏览器中打开
index.html
文件,查看控制台输出的结果。
通过这种方式,我们可以利用Web Worker在后台线程中处理耗时的任务,避免阻塞主线程,提高页面的响应性能。
其他方案就不一一举例了,有兴趣的可以自己了解一下!