- 对于动画效果的实现,避免使用 setTimeout 或 setInterval,请使用 requestAnimationFrame。
- 将长时间运行的 JavaScript 从主线程移到 Web Worker。
- 使用微任务来执行对多个帧的 DOM 更改。
- 使用 Chrome DevTools 的 Timeline 和 JavaScript 分析器来评估 JavaScript 的影响
使用 requestAnimationFrame 来实现视觉变化
/** * If run as a requestAnimationFrame callback, this * will be run at the start of the frame. */ function updateScreen(time) { // Make visual updates here. } requestAnimationFrame(updateScreen);
保证 JavaScript 在帧开始时运行的唯一方式是使用 requestAnimationFrame。
下次重绘之前执行 callback, callback 会接受一个当前执行的时间戳
某些框架或示例可能使用 setTimeout或 setInterval 来执行动画之类的视觉变化,但这种做法的问题是,回调将在帧中的某个时点运行,可能刚好在末尾,而这可能经常会使我们丢失帧,导致卡顿。
事实上,jQuery 目前的默认 animate行为是使用 setTimeout!强烈建议您打上补丁程序以使用 requestAnimationFrame。
降低复杂性或使用 Web Worker
var dataSortWorker = new Worker("sort-worker.js"); dataSortWorker.postMesssage(dataToSort); // The main thread is now free to continue working on other things... dataSortWorker.addEventListener('message', function(evt) { var sortedData = evt.data; // Update data on screen... });
并非所有工作都适合此模型:Web Worker 没有 DOM 访问权限。如果您的工作必须在主线程上执行,请考虑一种批量方法,将大型任务分割为微任务,每个微任务所占时间不超过几毫秒,并且在每帧的 requestAnimationFrame 处理程序内运行。
var taskList = breakBigTaskIntoMicroTasks(monsterTaskList); requestAnimationFrame(processTaskList); function processTaskList(taskStartTime) { var taskFinishTime; do { // Assume the next task is pushed onto a stack. var nextTask = taskList.pop(); // Process nextTask. processTask(nextTask); // Go again if there’s enough time to do the next task. taskFinishTime = window.performance.now();
// 渲染之前如果 js 执行时间小于3 可以在执行 下个 taskList 任务,如果大于3 就 放在下次渲染前执行 } while (taskFinishTime - taskStartTime < 3); if (taskList.length > 0) requestAnimationFrame(processTaskList); //递归 执行下次任务 }
此方法会产生 UX 和 UI 后果,您将需要使用进度或活动指示器来确保用户知道任务正在被处理。在任何情况下,此方法都不会占用应用的主线程,从而有助于主线程始终对用户交互作出快速响应。
避免微优化 JavaScript
知道浏览器执行一个函数版本比另一个函数要快 100 倍可能会很酷,比如请求元素的offsetTop比计算getBoundingClientRect()要快,但是,您在每帧调用这类函数的次数几乎总是很少,因此,把重点放在 JavaScript 性能的这个方面通常是白费劲。您一般只能节省零点几毫秒的时间。
如果您开发的是游戏或计算开销很大的应用,则可能属于本指南的例外情况,因为您一般会将大量计算放入单个帧,在这种情况下各种方法都很有用。
简而言之,慎用微优化,因为它们通常不会映射到您正在构建的应用类型。