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. macOS BLAS LAPACK

    /System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/Headers

  2. PHP截取字符串函数,根据dede修改而来

    dede中,有一个函数function cn_substr_utf8($str, $length, $start=0) 但测试时,并不如我所想的一样,可能是因为个人使用习惯吧.比如,字符串为数字或字母 ...

  3. JS大文件上传解决方案

    1 背景 用户本地有一份txt或者csv文件,无论是从业务数据库导出.还是其他途径获取,当需要使用蚂蚁的大数据分析工具进行数据加工.挖掘和共创应用的时候,首先要将本地文件上传至ODPS,普通的小文件通 ...

  4. USACO 2006 November Gold Corn Fields

    USACO 2006 November Gold Corn Fields 题目描述: Farmer John has purchased a lush new rectangular pasture ...

  5. Queue2链队列

    链队列 1 #include <iostream> using namespace std; template <class T> class Queue { private: ...

  6. UPDATE 在不同数据库中的使用方式

    MYSQL 中update 表一 set Gmoney = 表二.列名 from 表一,表二 where 表一.EMPID = 表二.EMPID举例:update table1 set table1. ...

  7. 6 October

    P1514 引水入城 题目描述 在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠.该国的行政区划十分特殊,刚好构成一个 \(N\) 行 \(\times M\) 列的矩形,如上图所示, ...

  8. noi.ac #227 random

    分析 我们发现实际只要计算a[i]>b[j]和a[i]<b[j]哪种多即可 代码 #include<bits/stdc++.h> using namespace std; ], ...

  9. webService接口的py文件打包成exe

    (一)webService接口的py文件打包成exe,在python3.5版本.pyInstaller3.2版本.pywin32-219.win-amd64-py3.5版本打包时报错,原因可能是pyi ...

  10. 137、TensorFlow使用TextCNN进行文本分类

    下面是分类的主函数入口 #! /usr/bin/env python import tensorflow as tf import numpy as np import os import time ...