Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[译]渲染性能优化之JS篇 #196

Open
FrankKai opened this issue Apr 1, 2020 · 0 comments
Open

[译]渲染性能优化之JS篇 #196

FrankKai opened this issue Apr 1, 2020 · 0 comments

Comments

@FrankKai
Copy link
Owner

FrankKai commented Apr 1, 2020

原文链接:https://developers.google.com/web/fundamentals/performance/rendering/optimize-javascript-execution#reduce_complexity_or_use_web_workers

  • 前言
  • TL;DR
  • 使用requestAnimationFrame处理可视改变
  • 降低复杂性或者使用Web Workers
  • 了解你的JavaScript ”frame tax“
  • 避免微优化你的JavaScript

TL;DR

  • 避免使用setTimeout或setInterval做可视更新;使用requestAnimationFrame
  • 从主线程移除长运行的js到web worker
  • 使用微任务在几个frame内对DOM进行更改
  • 使用Chrome DevTool的性能分析工具加速js的运行

使用requestAnimationFrame处理可视改变

当屏幕上发生可视改变时,你想让浏览器在正确的时间做事情,也就是在一个frame开始的时候。确保js在frame开始时运行的唯一方式是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);

框架或者例子也许会用setTimeout或setInterval去做类似动画的可视改变,但是问题是callback会在frame的某一个点上运行,也许会在结尾,可能会导致我们错过一个frame,导致jank。

image

事实上,jQuery在animate函数中用了setTimeout。在v3中换成了requestAnimationFrame。

降低复杂性或者使用Web Workers

js运行在浏览器的主线程上,包括样式计算,layout但是更多的时候是在做paint。如果你的js运行需要很长时间,它会阻塞其他的任务,会明显导致帧丢失。

在js运行时,你需要有自己的策略,比如说多久。例如,如果你在运行一个scrolling动画,理想情况的js运行最好在3ms到4ms。超过这个时间就占据太多了。如果是在空闲期,当然可以花费更多时间在上面。

很多情况下可以把一些纯计算的工作移动到web worker,比如不需要引用DOM节点的。数据处理或转换,比如排序和搜索,通常很适合。

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工作者没有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();
  } while (taskFinishTime - taskStartTime < 3);

  if (taskList.length > 0)
    requestAnimationFrame(processTaskList);

}

这种方法会产生UX和UI方面的后果,您需要确保用户知道正在处理的任务,方法是使用进度或活动指示器。在任何情况下,这种方法将保持你的应用程序的主线程自由,帮助它保持对用户交互的响应。

具体如何使用webworker,可以参考:一次失败的用web worker提升速度的实践

了解你的JavaScript ”frame tax“

在使用js框架,库或者自己的代码的时候,需要逐帧知道它们的消耗。
对于transitioning和scrolling这种性能严格的动画,知道消耗是很重要的。

image

在Main里面详细标明了每段代码消耗了多少时间。

避免微优化你的JavaScript

知道浏览器可以执行的一个版本比另一件东西快100倍是一件很酷的事,请求元素的offsetTop比getBoundingClientRect()更快,但这是几乎总是正确的。你只会被调用函数这样一个小的每帧,所以通常浪费努力专注于这方面的JavaScript的性能。通常只会节省几毫秒的时间。

如果你正在制作一款游戏,或者是一个需要大量计算的应用程序,那么你很可能是这个指南的一个例外,因为你通常会将大量的计算放入一个单独的框架中,在这种情况下,一切都是有帮助的。

简而言之,您应该对微优化非常谨慎,因为它们通常不会反应到您正在构建的应用程序的类型。

核心思想就是:不要花费巨大的努力在微小的优化上,耗时耗力而且效果不明显。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant