1.不要一条一条地修改 DOM 的样式。与其这样,还不如预先定义好 css 的 class,然后修改 DOM 的 className。

2)把 DOM 离线后修改。如:

  • 使用 documentFragment 对象在内存里操作 DOM。
  • 先把 DOM 给 display:none (有一次 repaint),然后你想怎么改就怎么改。比如修改 100 次,然后再把他显示出来。
  • clone 一个 DOM 结点到内存里,然后想怎么改就怎么改,改完后,和在线的那个的交换一下。

3)不要把 DOM 结点的属性值放在一个循环里当成循环里的变量。不然这会导致大量地读写这个结点的属性。

4)尽可能的修改层级比较低的 DOM。当然,改变层级比较底的 DOM 有可能会造成大面积的 reflow,但是也可能影响范围很小。

5)为动画的 HTML 元件使用 fixed 或 absoult 的 position,那么修改他们的 CSS 是不会 reflow 的。

6)千万不要使用 table 布局。因为可能很小的一个小改动会造成整个 table 的重新布局。

  • 1.优化JavaScript的执行效率
  • 1.1动画实现,避免使用setTimeout或setInterval,尽量使用requestAnimationFrame
  • var start = null;
    var element = document.getElementById('SomeElementYouWantToAnimate');
    element.style.position = 'absolute'; function step(timestamp) {
    if (!start) start = timestamp;
    var progress = timestamp - start;
    element.style.left = Math.min(progress / 10, 200) + 'px';
    if (progress < 2000) {
    window.requestAnimationFrame(step);
    }
    } window.requestAnimationFrame(step);
  • 1.2把耗时长的JavaScript代码放到Web Workers中去做
  • JavaScript代码运行在浏览器的主线程上,与此同时,浏览器的主线程还负责样式计算、布局、绘制的工作,如果JavaScript代码运行时间过长,就会阻塞其他渲染工作,很可能会导致丢帧。
    前面提到每帧的渲染应该在16ms内完成,但在动画过程中,由于已经被占用了不少时间,所以JavaScript代码运行耗时应该控制在3-4毫秒。
    如果真的有特别耗时且不操作DOM元素的纯计算工作,可以考虑放到Web Workers中执行。
  •  
    代码实现:
    var dataSortWorker = new Worker("sort-worker.js"); dataSortWorker.postMesssage(dataToSort); // 主线程不受Web Workers线程干扰 dataSortWorker.addEventListener('message', function(evt) { var sortedData = e.data; // Web Workers线程执行结束 // ... });
    1.3把DOM元素的更新划分为多个小任务,分别在多个frame中去完成

    由于Web Workers不能操作DOM元素的限制,所以只能做一些纯计算的工作,对于很多需要操作DOM元素的逻辑,可以考虑分步处理,把任务分为若干个小任务,每个任务都放到requestAnimationFrame中回调执行

    var taskList = breakBigTaskIntoMicroTasks(monsterTaskList);
    
    requestAnimationFrame(processTaskList);
    
    function processTaskList(taskStartTime) {
    var nextTask = taskList.pop(); // 执行小任务
    processTask(nextTask); if (taskList.length > 0) {
    requestAnimationFrame(processTaskList);
    }
    }
  • 2.降低样式计算的范围和复杂度
  • 2.1降低样式选择器的复杂度

    尽量保持class的简短,或者使用Web Components框架。

    .box:nth-last-child(-n+1) .title {
    }
    // 改善后
    .final-box-title {
    }
    2.2减少需要执行样式计算的元素个数

    由于浏览器的优化,现代浏览器的样式计算直接对目标元素执行,而不是对整个页面执行,所以我们应该尽可能减少需要执行样式计算的元素的个数

  • 3.避免大规模、复杂的布局
  • 3.1尽可能避免触发布局

    当你修改了元素的属性之后,浏览器将会检查为了使这个修改生效是否需要重新计算布局以及更新渲染树,对于DOM元素的“几何属性”修改,比如width/height/left/top等,都需要重新计算布局。

  • 3.2使用flexbox替代老的布局模型
  • 3.3避免强制同步布局事件的发生
  • 渲染屏幕的流程
  • 首先是JavaScript脚本,然后是Style,然后是Layout,但是我们可以强制浏览器在执行JavaScript脚本之前先执行布局过程,这就是所谓的强制同步布局。
  • requestAnimationFrame(logBoxHeight);
    
    // 先写后读,触发强制布局
    function logBoxHeight() {
    // 更新box样式
    box.classList.add('super-big'); // 为了返回box的offersetHeight值
    // 浏览器必须先应用属性修改,接着执行布局过程
    console.log(box.offsetHeight);
    } // 先读后写,避免强制布局
    function logBoxHeight() {
    // 获取box.offsetHeight
    console.log(box.offsetHeight); // 更新box样式
    box.classList.add('super-big');
    }

    在JavaScript脚本运行的时候,它能获取到的元素样式属性值都是上一帧画面的,都是旧的值。因此,如果你在当前帧获取属性之前又对元素节点有改动,那就会导致浏览器必须先应用属性修改,结果执行布局过程,最后再执行JavaScript逻辑。

    3.4避免连续的强制同步布局发生

    如果连续快速的多次触发强制同步布局,那么结果更糟糕。
    比如下面的例子,获取box的属性,设置到paragraphs上,由于每次设置paragraphs都会触发样式计算和布局过程,而下一次获取box的属性必须等到上一步设置结束之后才能触发。

  • function resizeWidth() { // 会让浏览器陷入'读写读写'循环 for (var i = 0; i < paragraphs.length; i++) { paragraphs[i].style.width = box.offsetWidth + 'px'; } } // 改善后方案 var width = box.offsetWidth; function resizeWidth() { for (var i = 0; i < paragraphs.length; i++) { paragraphs[i].style.width = width + 'px'; } }
  • 4.简化绘制的复杂度、减少绘制区域
  • 绘制就是填充像素的过程,通常这个过程是整个渲染流程中耗时最长的一环,因此也是最需要避免发生的一环。
    如果Layout被触发,那么接下来元素的Paint一定会被触发。当然纯粹改变元素的非几何属性,也可能会触发Paint,比如背景、文字颜色、阴影效果等。
  • 5.优先使用渲染层合并属性、控制层数量
  • 5.1使用transform/opacity实现动画效果

    使用transform/opacity实现动画效果,会跳过渲染流程的布局和绘制环节,只做渲染层的合并。

  • 6.对用户输入事件的处理函数去抖动(移动设备)
  • 6.1避免使用运行时间过长的输入事件处理函数

    理想情况下,当用户和页面交互,页面的渲染层合并线程将接收到这个事件并移动元素。这个响应过程是不需要主线程参与,不会导致JavaScript、布局和绘制过程发生。

  • 但是如果被触摸的元素绑定了输入事件处理函数,比如touchstart/touchmove/touchend,那么渲染层合并线程必须等待这些被绑定的处理函数执行完毕才能执行,也就是用户的滚动页面操作被阻塞了,表现出的行为就是滚动出现延迟或者卡顿。

    简而言之就是你必须确保用户输入事件绑定的任何处理函数都能够快速的执行完毕,以便腾出时间来让渲染层合并线程完成他的工作。

    6.2避免在输入事件处理函数中修改样式属性

    输入事件处理函数,比如scroll/touch事件的处理,都会在requestAnimationFrame之前被调用执行。
    因此,如果你在上述输入事件的处理函数中做了修改样式属性的操作,那么这些操作就会被浏览器暂存起来,然后在调用requestAnimationFrame的时候,如果你在一开始就做了读取样式属性的操作,那么将会触发浏览器的强制同步布局操作。

  • 参考文章:https://www.jianshu.com/p/a32b890c29b1

浏览器如何减少 reflow/repaint的更多相关文章

  1. 如何优化你的JS脚本来减少reflow/repaint?

    如何优化你的脚本来减少reflow/repaint?1. 避免在document上直接进行频繁的DOM操作,如果确实需要可以采用off-document的方式进行,具体的方法包括但不完全包括以下几种: ...

  2. 浏览器渲染原理--reflow

    Web页面运行在各种各样的浏览器当中,浏览器载入.渲染页面的速度直接影响着用户体验简单地说,页面渲染就是浏览器将html代码根据CSS定义的规则显示在浏览器窗口中的这个过程.先来大致了解一下浏览器都是 ...

  3. ♫【网站优化】Reflow / Repaint

    web移动开发最佳实践之js篇 浏览器的回流与重绘 by 张盛志 DOM性能瓶颈与Javascript性能优化 浏览器的渲染原理简介 其中一个跟浏览器有关的原因,那就是浏览器需要花时间.花精力去渲染. ...

  4. 浏览器的回流与重绘 (Reflow & Repaint)

    写在前面 在讨论回流与重绘之前,我们要知道: 浏览器使用流式布局模型 (Flow Based Layout). 浏览器会把HTML解析成DOM,把CSS解析成CSSOM,DOM和CSSOM合并就产生了 ...

  5. 浏览器的重绘与回流(Reflow & Repaint)介绍

    重绘 当页面元素样式改变不影响元素在文档流中的位置时(如background-color,border-color,visibility),浏览器只会将新样式赋予元素并进行重新绘制操作. 回流 当改变 ...

  6. 介绍回流与重绘(Reflow & Repaint),以及如何进行优化?

    前言 回流与重绘对于前端来说可以说是非常重要的知识点了,我们不仅需要知道什么是回流与重绘,还需要知道如何进行优化.一个页面从加载到完成,首先是构建DOM树,然后根据DOM节点的几何属性形成render ...

  7. Reflow & Repaint

    http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/ http://segmentfault.com/a/1190000002 ...

  8. web前端优化手段

    web前端优化手段有很多,同种的优化方式或许在不同的网络协议会南辕北辙,下面就自己结合工作经验和学习总结的一些手段总结 1.合并文件减小请求数:sprite图片的合成.合并脚本与样式. 2.减小文件的 ...

  9. HTML(总结)

    HTML 浏览器内核有哪些 Trident:IE Gecko:Firefox Webkit:Chrome Safari Presto:Opera(投奔Webkit) html5的一些新特性 1. 拖拽 ...

随机推荐

  1. 记录一下LEETheme库主题切换的使用

    LEETheme库的位置:https://github.com/lixiang1994/LEETheme 从https://github.com/gsdios的SDAutolayoutDemo 中的白 ...

  2. zabbix创建钉钉报警

    https://juejin.im/post/5c8708eef265da2dd168a1c1 简介 家里搭建了zabbix去监控主机,邮件报警什么的都太麻烦了,所以直接使用钉钉机器人来提醒,下面是我 ...

  3. c#类的定义,c#中的关健字,C#标识符

    什么是类:一种数数据结构,存储数据成员,方法成员,和其它的内容,便 于方便 谳用C#语法: class 类名{ //TODO} C#中关键字(小写)不能作为方法名,类名,命名空间名等, static ...

  4. 虚拟机CentOS7安装docker并搭建Gitlab私服

    一.下载安装虚拟机和CentOS7系统 这些流程比较简单不会有什么坑,这里不再阐述 二.安装docker 1.Docker 要求 CentOS 系统的内核版本高于 3.10 ,查看本页面的前提条件来验 ...

  5. [CF1045A] Last chance

    题目:Last chance 传送门:http://codeforces.com/contest/1045/problem/A 分析: 1)有$n$个敌方飞船,己方有$m$个武器,有三种类型. 2)$ ...

  6. html简单标签代码

     html简单标签代码demo <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "ht ...

  7. linux 统计代码行数

    列出目录下所有文件(仅有文件名):ls -laR 列出目录下所有文件名称find . * 统计当前目录下全部代码行数find . * | xargs wc -l 统计当前目录下java文件行数,去除空 ...

  8. 由react循环总结的小知识

    const listItems = numbers.map((numbers,index)=><li key={index}>{numbers}</li>)   cons ...

  9. vs2019里没有linq to sql或EF工具,导致dbml或者edmx无法通过设计器浏览

    点击:工具->获取工具或功能 选择需要安装的工具,然后点击底部的修改按钮就可以了,等待安装完成,如下图:

  10. Spring MVC浅析

    讲到MVC,想必大家都很熟悉,就是将数据模型.视图.控制器进行分离,做到分工明确,在Spring的帮助下,Spring MVC 更是做到了充分的解耦,因为大部分的资源都由Spring进行管理,为Spr ...