x-note
  • Introduction
  • JavaScript
    • JavaScript 作用域链
    • JavaScript 数据结构与类型
    • JavaScript 原型
    • JavaScript this 关键字
    • JavaScript 函数
    • JavaScript delete 运算符
    • JavaScript 内存管理与垃圾回收
    • JavaScript 严格模式与混乱模式
    • JavaScript 数字精度丢失
    • JavaScript 并发模型
    • 利用原型链实现继承
  • ECMAScript
    • ECMAScript 6 变量及常量的声明
    • ECMAScript 6 变量的解构赋值
    • ECMAScript 6 Promise 对象
    • ECMAScript 6 Symbol
    • ECMAScript 6 Proxy
    • ECMAScript 6 Reflect
    • ECMAScript 6 new.target
    • ECMAScript 6 Set 和 WeakSet
    • ECMAScript 6 Map 和 WeakMap
    • ECMAScript 6 Iterator
    • ECMAScript 6 Generator
    • ECMAScript 6 class
    • ECMAScript 7
    • ECMAScript 8 async 函数
    • ECMAScript 8 内存共享与原子性
    • ECMAScript 8 Others
    • ECMAScript 2018
    • ECMAScript 2019
  • CSS
    • CSS 块格式化上下文(BFC)
    • CSS 盒模型
    • CSS 外边距合并
    • CSS Float
    • CSS Position
    • CSS Border-Image
    • CSS BEM
    • CSS 表布局详解
    • 页面布局之单列布局
    • 页面布局之多列布局
  • React
    • React 组件的生命周期
    • React 虚拟 DOM
    • React Reconciliation
    • React Diff 算法核心
    • React Fiber
    • React Scheduling
    • React Context API
    • React Refs
    • React HMR
    • React Hook
  • VUE
    • VUE 响应式系统
    • VUE 渲染机制
    • 关于 Vue 的思考
  • Webpack
    • Webpack 基本概念
    • Webpack HMR
  • Babel
    • @babel/preset-env
  • WEB
    • WEB 基础知识及概念
      • 屏幕测量单位
      • 重绘与重排
      • 前端模块化系统
      • WEB 客户端存储
      • 浏览器的渲染过程
    • WEB 性能优化
      • WEB 性能指标
      • WEB 图片优化
      • 懒加载资源
    • WEB 安全
      • XSS
      • XSRF
      • 点击劫持
      • 同源策略(Same Origin Policy,SOP)
    • WEB 解决方案
      • webp 兼容方案
      • WEB 拖拽实现方案
    • WEB SEO
  • Git
    • Git 工作流
    • Git 内部原理
  • 传输协议
    • UDP
      • UDP 基本概念
    • TCP
      • TCP 基本概念
    • HTTP
      • HTTP 基础
      • HTTP 缓存
      • HTTP-2
      • HTTP-3
      • HTTPS
      • 自定义 HTTPS 证书
  • Protocol Buffers
    • Protocol Buffers 基础
  • gRPC
    • gRPC 简介
    • gRPC 基础概念
    • GRPC with GraphQL and TypeScript
  • 正则表达式
    • 正则表达式基础
    • 正则表达式的悲观回溯
  • 基础算法
    • 冒泡排序
    • 插入排序
    • 选择排序
    • 快速排序
    • 归并排序
    • 希尔排序
    • 堆排序
    • 桶排序
    • 计数排序
    • 基数排序
    • 二叉树的遍历
    • 动态规划
    • 回溯
  • 压缩算法
    • HPACK
    • QPACK
  • 设计模式
    • DDD
      • 模型元素的模式
    • 常见设计模式
      • 工厂方法
      • 抽象工厂
      • 构造器
      • 原型
      • 单例模式
      • 适配器模式
      • 桥接模式
      • 组合模式
      • 外观模式
      • 享元模式
      • 代理模式
      • 责任链模式
      • 命令模式
      • 迭代器模式
      • 中介者模式
      • 备忘录模式
      • 观察者模式
      • 状态模式
      • 策略模式
      • 模版方法模式
      • 访问者模式
      • 依赖注入
    • MVC
    • MVP
    • MVVM
  • 颜色空间
    • LCH
由 GitBook 提供支持
在本页
  • 重排
  • 重绘
  • 关于重绘与重排优化的切入点
  • 减少盒模型操作
  • 批量修改元素
  • Intersection Observer
  • Tools
在GitHub上编辑
  1. WEB
  2. WEB 基础知识及概念

重绘与重排

上一页屏幕测量单位下一页前端模块化系统

最后更新于5年前

当浏览器下载完页面所有的资源后,浏览器的 HTML 解析器会开始构建 DOM Tree,同时 CSS 解析器会构建 Style Tree。合并这两个 Tree 上的信息并最终形成 Render Tree。

PS:RenderLayerTree???

一旦 Render Tree 构建完成,浏览器便开始绘制(paint)页面元素。

重排

当元素的几何属性发生变化,浏览器重新计算元素的几何属性的过程就称之为重排。 eg:改变了元素宽度变化,内容增加导致行数增加(高度增加)

涉及到盒模型的 CSS 属性基本会造成重排。

读取元素的某些属性(offsetLeft、offsetTop、offsetHeight、offsetWidth、scrollTop/Left/Width/Height、clientTop/Left/Width/Height、getComputedStyle()、currentStyle(in IE)); 也会造成重排

重绘

当元素发生变化浏览器将其重新绘制到屏幕上的过程称之为重绘。

重排一定会引起重绘,重绘不一定伴随重排。

关于重绘与重排优化的切入点

重绘和重排操作都是代价昂贵的操作,会消耗 WEB 应用的性能。

减少盒模型操作

var $ele = document.querySelector(".ele");

// before
$ele.style.borderLeft = "1px";
$ele.style.borderRight = "2px";
$ele.style.padding = "5px";
// 大多数现代浏览器对上面代码进行了优化,但是老版浏览器可能导致严重的性能问题

// after
$ele.style.cssText = "border-left: 1px; border-right: 2px; padding: 5px;"; // 一次性修改

// or
$ele.className = "newClass";

批量修改元素

基本思路:

  • 让该元素脱离文档流

  • 对其进行多重改变

  • 将元素带回文档中

隐藏元素,进行修改后,然后再显示该元素

function appendNode($node, data) {
  var a, li;

  for (let i = 0, max = data.length; i < max; i++) {
    a = document.createElement("a");
    li = document.createElement("li");
    a.href = data[i].url;

    a.appendChild(document.createTextNode(data[i].name));
    li.appendChild(a);
    $node.appendChild(li);
  }
}

// before
let ul = document.querySelector("#mylist");
appendNode(ul, data);

// after
let ul = document.querySelector("#mylist");
ul.style.display = "none";
appendNode(ul, data);
ul.style.display = "block";

使用文档片段创建一个子树,然后再拷贝到文档中

let fragment = document.createDocumentFragment();
appendNode(fragment, data);
ul.appendChild(fragment);

将原始元素拷贝到一个独立的节点中,操作这个节点,然后覆盖原始元素

let old = document.querySelector("#mylist");
let clone = old.cloneNode(true);
appendNode(clone, data);
old.parentNode.replaceChild(clone, old);

others

  • 移动通过 translate 来实现

  • 浏览器原理中(root 节点,css position:relative,absolute,transform,有节点溢出 overflow,alpha mask,reflection,css filter,2dCanvas,WebGl,video) 都会为这些元素创造一个新的 Layer 以便浏览器加速渲染,这些元素的修改只会涉及自己重排重绘。

Intersection Observer

Intersection Observer API 提供了一种异步观察目标元素与祖先元素或顶级文档 viewport 的交集中的变化的方法。

多种情况下都需要用到元素交集变化的信息,比如:

  • 当页面滚动时,懒加载图片或其他内容。

  • 实现 “可无限滚动” 网站,也就是当用户滚动网页时直接加载更多内容,无需翻页。

  • 为计算广告收益,检测其广告元素的曝光情况。

  • 根据用户是否已滚动到相应区域来灵活开始执行任务或动画。

获取元素的盒模型相关属性时也会导致 re-flow

<div id="parent" style="padding-top: 100vh;">
  <img
    data-src="https://www.baidu.com/img/baidu_jgylogo3.gif"
    width="200px"
    height="200px"
    alt="xxx"
  />
  <img
    data-src="https://www.baidu.com/img/baidu_jgylogo3.gif"
    width="200px"
    height="200px"
    style="margin-top:100vh;"
    alt="xxx"
  />
</div>
<script>
  const options = {
    root: document,
    rootMargin: "0px",
    threshold: 1.0,
  };

  const observer = new IntersectionObserver((entries, observer) => {
    if (entries.length < 1) {
      observer.disconnect();
    }
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        const target = entry.target;
        observer.unobserve(target);
        if (!target.src) {
          target.src = target.dataset.src;
        }
      }
    });
  }, options);

  const $imgs = document.querySelectorAll("img");
  $imgs.forEach((item) => {
    observer.observe(item);
  });
</script>

Tools

Site Report