前端八股文 怎样能不懂一点呢~
这是前端面试 最喜欢问的问题, 让咱们一起看一下~

前端在做UI交互效果的时分会不会存在这样一个质疑:这样到底做好不好❓
主要是两个方面导致质疑的存在(~~嘿嘿,实践上是想更优秀)

  • 一个是页面布局方面(此处不深化哦~);
  • 一个是浏览器烘托方面(会不会触发重绘和重排❓)

此刻的你可能会有些疑问:

  • 什么是浏览器的重绘重排❓

  • 怎样在实践项目中把重绘重排引起的功能问题考虑进去❓

  • 怎样权衡重绘与重排❓

    以下能够部分解释以上三个疑问:

重绘 和 重排(回流)

重绘不必定导致重排,但重排必定会导致重绘
重排(Reflow) && 重绘(Redraw)会付出高昂的功能价值

重绘

重绘 (Redraw:某些元素的外观被改动所触发的浏览器行为(从头核算节点在屏幕中的绝对方位并烘托的过程); 例如:修正元素的填充颜色,会触发重绘;

重排(回流)

重排 (Reflow:从头生成布局,从头排列元素(从头核算各节点和css具体的巨细和方位:烘托树需求从头核算一切受影响的节点**);例如:改元素的宽高,会触发重排;
经过两者概念差异显着得知,重排要比重绘的成本大得多,咱们应该尽量削减重排操作,削减页面功能耗费

那些操作会导致重绘与重排 ❓

下面状况会产生重绘:

  • color
  • border-style
  • border-radius
  • text-decoration
  • box-shadow
  • outline
  • background

下面状况会产生重排:

  • 页面初始烘托,这是开销最大的一次重排;
  • 增加/删去可见的DOM元素;
  • 改动元素方位;
  • 改动元素尺度,比方边距、填充、边框、宽度和高度等;
  • 改动元素内容,比方文字数量,图片巨细等;
  • 改动元素字体巨细;
  • 改动浏览器窗口尺度,比方resize事情产生时;
  • 激活CSS伪类(例如::hover);
  • 设置 style 特点的值,因为经过设置style特点改动结点款式的话,每一次设置都会触发一次reflow;
  • 查询某些特点或调用某些核算方法:offsetWidth、offsetHeight等,除此之外,当咱们调用getComputedStyl方法,或许IE里的 currentStyle 时,也会触发重排,原理是一样的,都为求一个“即时性”和“准确性”;

重排影响的规模:

  • 大局规模(大局布局):从根节点html开端对整个烘托树进行从头布局;
  • 局部规模(局部布局):对烘托树的某部分或某一个烘托目标进行从头布局;

优化建议:

核心观念: 削减重排次数和减小重排规模

款式集中改动(削减重排次数):

<!-- html -->
<span id="demo">
  我是demo
</span>
// javascript
// renderEle.style 逐个增加/修正特点值
const renderEle = d、ocument.getElementById('demo');
renderEle.style.color = 'red'; // 导致重绘
renderEle.style.background= '#ccc'; // 导致重绘
renderEle.style.padding = '15px 20px'; // 导致重排(重排会引起重绘)

以上操作会导致 3次重绘 1次重排; 能够动态增加class,只会导致1次重排(重排会引起重绘),然后削减重绘次数;
能够合并为:

// javascript
document.getElementById('demo').className = 'demo'; // 增加class 统一增加/修正款式
/* css */
.demo {
  color: red;
  background: #ccc;
  padding: 15px 20px;
}

将 DOM 离线(削减重排次数

离线操作DOM:当对DOM 节点有较大改动的时分,咱们先将元素脱离文档流,然后对元素进行操作,最终再把操作后的元素放回文档流。
1. 修正DOM节点的display特点,暂时将此节点从文档流中脱离,然后再恢复;

<!-- html -->
<span id="demo">
  我是demo
</span>

需求频频操作DOM 修正style

  // javascript
  // 第一次操作修正 color、background、padding
  const renderEle = document.getElementById('demo');
  renderEle.style.color = 'red'; // 导致重绘
  renderEle.style.background= '#ccc'; // 导致重绘
  renderEle.style.padding = '15px 20px'; // 导致重排(重排会引起重绘)
  // ...
  // 第2次操作修正 marginLeft、marginTop
  const renderEle = document.getElementById('demo');
  renderEle.style.marginLeft = '15px'; // 导致重排(重排会引起重绘)
  renderEle.style.marginTop = '15px'; // 导致重排(重排会引起重绘)
  // ...
  // 第三次操作修正 border
  const renderEle = document.getElementById('demo');
  renderEle.style.border = '2px solid #ccc'; // 导致重排(重排会引起重绘)

以上操作触发屡次重排、重绘; 能够将renderEle进行离线操作; 修正如下:

  // javascript
  const renderEle = document.getElementById('demo');
  // 第一次操作修正 color、background、padding
  renderEle.style.display = 'none'; // 导致重排(重排会引起渲)
  renderEle.style.color = 'red'; // DOM不存在烘托树上不会引起重排、重绘
  renderEle.style.background= '#ccc';// DOM不存在烘托树上不会引起重排、重绘
  renderEle.style.padding = '15px 20px';// DOM不存在烘托树上不会引起重排、重绘
  // ...
  // 第2次操作修正 marginLeft、marginTop
  renderEle.style.marginLeft = '15px';// DOM不存在烘托树上不会引起重排、重绘
  renderEle.style.marginTop = '15px';// DOM不存在烘托树上不会引起重排、重绘
  // ...
  // 第三次操作修正 border
  renderEle.style.border = '2px solid #ccc';// DOM不存在烘托树上不会引起重排、重绘
  renderEle.style.display = 'block';// 导致重排(重排会引起渲)

以上对躲藏的DOM元素操作不会引发其他元素的重排,这样只在躲藏和显现时触发2次重排。

脱离文档流: 使用 absolute 或 fixed 脱离文档流(减小重排规模):

<!-- html -->
<div id='demo'>
  <span id="demo-one">
    我是demo 1号
  </span>
  <span id="demo-two">
    我是demo 2号
  </span>
  <span id="demo-there">
    我是demo 3号
  </span>
</div>
// javascript
const renderEle = document.getElementById('demo-one');
renderEle.style.padding = '15px 20px'; // 导致重排(重排会引起重绘)
renderEle.style.height = '60px'; // 导致重排(重排会引起重绘)

将需求重排的元素,position特点设为absolute或fixed(某些特殊场合),减小重排规模。

  // javascript
  const renderEle = document.getElementById('demo-one');
  renderEle.style.position = 'fixed'; // 导致重排(重排会引起重绘)
  renderEle.style.padding = '15px 20px'; // 导致重排(只要当时元素)
  renderEle.style.height = '60px'; // 导致重排(只要当时元素)

这样此DOM元素就脱离了文档流,它的变化不会影响到其他元素。

善用内存:在内存中屡次操作DOM,再整个增加到DOM树(减小重排规模)

举例:异步恳求接口获取数据,动态烘托到页面

<!-- html -->
<div id="demo">
  <ul id="father">
    <li>我是0号,我后边还有1号、2号、3号、4号、5号</li>
  </ul>
</div>
// javascript
const ulEle = document.getElementById("father");
let arr = [];
setTimeout( () => {
  arr = "我是0号,我后边还有1号,2号,3号,4号,5号", "我是2号", "我是3号", "我是4号", "我是5号"]; // 我是动态获取的
  arr.forEach(element => {
    const childNode = document.createElement('li');
    childNode.innerText = element;
    ulEle.appendChild(childNode);// 每一次都会引起重排(重排会引起重绘)
  })
},1000)

导致屡次重排; 能够进行以下修正(构建整个ul,而不是循环增加li):

<!-- html -->
<div id="demo"></div>
// javascript
const ulEle = document.getElementById("demo");
const childUlNode = document.createElement('ul');
let arr = [];
setTimeout(() => {
  arr = ["我是0号,我后边还有1号,2号,3号,4号,5号","我是1号", "我是2号", "我是3号", "我是4号", "我是5号"]; // 我是动态获取的
   arr.forEach(element => {
     const childLiNode = document.createElement('li');
     childLiNode.innerText = element;
     childUlNode.appendChild(childLiNode);
   })
},1000)
ulEle.appendChild(childUlNode);// 只会引起一次重排(重排会引起重绘)

读写别离:将写入的值缓存,读取缓存的值(削减重排次数

有一些浏览器针对重排做出来优化。
比方Opera:当你触发重排的条件抵达必定量的时分, 或许比及必定时刻的时分,或许等一个线程结束,再一起进行重排;但除了烘托树的直接变化,当获取一些特点时,浏览器为取得正确的值也会触发重排。这样就使得浏览器的优化失效了;

<!-- html -->
<span id="demo">
  我是demo
</span>
  // javascript
  const offsetWidth = '100px';
  const renderEle = document.getElementById('demo');
  renderEle.style.offsetWidth = offsetWidth // 导致重绘(写入)
  const tempoOffsetWidth = renderEle.style.offsetWidth // 读取可能会导致重排

上述代码中可使用读写别离(写入值的时分进行缓存),防止屡次重排;

  // javascript
  const offsetWidth = '100px';
  const renderEle = document.getElementById('demo');
  renderEle.style.offsetWidth = offsetWidth // 导致重绘(写入)
  const tempoOffsetWidth = renderEle; // 防止直接读取offsetWidth

前端面试  第四篇  重绘和重排(回流)
真的不能再加班了,要跑路了,内卷太可怕,耗费不起了,兄弟们 鄙人告辞!!!