前端八股文 怎样能不懂一点呢~
这是前端面试 最喜欢问的问题, 让咱们一起看一下~
前端在做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
真的不能再加班了,要跑路了,内卷太可怕,耗费不起了,兄弟们 鄙人告辞!!!