讀書筆記 --- 再次閱讀回流與重繪

參考 - 強烈推薦看看,這個作者寫了很多特別好的文章.

瀏覽器渲染過程

  1. 解析HTML,生成DOM樹; 解析CSS生成CSSOM樹
  2. 將DOM樹和CSSOM樹合并,生成渲染(Render)樹
  3. Layout(回流): 根據生成的渲染樹,視口(viewport),得到節點的幾何信息(位置、大小)
  4. Painting(重繪): 根據渲染樹和幾何信息得到節點的絕對像素
  5. Display: 將像素發送給GPU,展示在頁面上

生成渲染樹

image-20200221150055606

為了構建渲染樹,瀏覽器主要完成了以下工作:

  1. 從DOM樹的根節點開始遍歷每個可見節點
  2. 對于每個可見的節點,找到CSSOM樹中的規則,并應用它們
  3. 根據每個可見節點及其對應的樣式,組合生成渲染樹

【不可見的節點】:

  • 一些不會渲染輸出的節點: 比如script、meta、link等
  • 一些通過css進行隱藏的節點。比如display: none。注意,利用visibility和opacity隱藏的節點,還是會顯示在渲染樹上的。只有display:none的節點才不會顯示在渲染樹上

【注意】: 渲染樹只包括可見的節點

回流(Layout)

前面將DOM節點以及它對應的樣式結合起來,可是我們還需要計算它們在設備視口(viewport)內的確切位置和大小,這個計算的階段就是回流。看下面的栗子:

<!DOCTYPE html>
<html><head><meta name="viewport" content="width=device-width,initial-scale=1"><title>Cretical Path: Hello Marron!</title></head><body><div style="width: 50%"><div style="width: 50%">Hi Marron, best wish!</div></div></body>
</html>

我們可以看到,第一個div將節點的顯示尺寸設置為視口寬度的50%,第二個div將其尺寸設置為父節點的50%.而在回流這個階段,我們就需要根據視口具體的寬度,將其轉為實際的像素值。

? preview

重繪 (Painting)

  • 生成渲染樹階段: 我們直到了哪些節點是可見的以及可見節點的樣式
  • 在回流階段: 我們得到了可見元素的具體幾何信息

我們得到的信息,最終都會托付給GPU進行渲染

GPU的渲染需要具體的像素位置,這就是重繪階段所做的事情: 根據渲染樹和幾何信息計算出絕對像素點.

何時發生回流重繪

回流主要是計算節點的幾何位置和幾何像素大小.那么當頁面布局和幾何信息發生變化的時候,就需要回流:

  • 添加或刪除可見的DOM元素
  • 元素的位置發生變化
  • 元素的尺寸發生變化(內/外邊距、邊框大小、高度和寬度等)
  • 內容發生: 文本發生變化或圖片被另一個不同尺寸的圖片所替代
  • 頁面剛開始渲染的時候
  • 瀏覽器的窗口尺寸變化: 回流是根據視口的大小來計算元素的位置和大小的

經典老話: 回流一定重繪,重繪不一定回流

瀏覽器的優化機制

現代的瀏覽器都是很聰明的,由于每次重排都會造成造成額外的計算消耗,因此大多數瀏覽器都會通過隊列修改、批量執行來優化重排過程。瀏覽器會將修改操作放在隊列里,直到過了一段時間,或者操作達到一個閾值,才清空隊列。

還有一些強制刷新的屬性(避免使用):

  • offsetTop、offsetLeft、offsetWidth、offsetHeight
  • scrollTop、scrollLeft、scrollWidth、scrollHeight
  • clientTop、clientLeft、clientWidth、clientHeight
  • getComputedStyle()
  • getBoundingClientRect

前端優化

1 -【并多次的DOM和添加樣式】

// 未優化前 - 3次
const el = document.getElementById('test')
el.style.padding = '5px';
el.style.borderLeft = '1px';
el.style.borderRight = '2px';// 合并樣式 - 1次
const el = document.getElementById('test');
el.style.cssText += 'border-left: 1px; border-right: 2px; padding: 5px'// 添加樣式 - 1次
const el = document.getElementById('test');
el.calssName += ' active';

2 -【脫離文檔流】

當元素脫離文檔流后,對元素的所有操作都不會引起回流和重繪.因此如果,對某個元素進行的DOM操作比較多的時候,可以先將元素脫離文檔流,然后操作,最后在放回文檔流。具體操作如下:

  1. 使元素脫離文檔流
  2. 對其進行多次修改
  3. 將元素帶回到文檔中.

[注] : 上述的1、3會引起回流和重繪.

【脫離文檔流的方法】

  • 隱藏元素,修改應用,重新顯示
  • 使用文檔片段(document fragment)在使用DOM之外構建一個子樹,再把它拷貝回文檔
  • 將原始元素拷貝到一個脫離文檔的節點中,修改節點后,再替換原始的元素。
// 每次插入li都會引起一次回流和重繪
function appendDataToElement(appendToElement, data) {let li;for(let i =0,len = data.length;i < len;i++){li = document.createElement('li');li.textContent = 'text';appendToElement.appendChild(li);}
}const ul = document.getElementById('list');
appdenDataToElement(ul, data);

[隱藏元素]

// 僅在隱藏元素和現實元素時產生2次回流和重繪
function appendDataToElement(appendToElement, data) {let li;for(let i =0, len = data.length; i < len; i++){li = document.createElement('li');li.textContent = 'text';appendTOElement.appendChild(li);}
}const ul = document.getElementById('list');
ul.style.display = 'none';
appendDataToElement(ul, data);
ul.style.display = 'block';

[使用文檔片段] - 在當前DOM外構建一個子樹,再把它拷貝回文檔

const ul = document.getElementById('list');
const fragment = document.createDocumentFragment();
appendDataToElement(fragment, data);
up.appendChild(fragment);

[脫離文檔] - 將原始元素拷貝到一個脫離文檔的節點中,修改節點,再替換原始的元素。

const ul = document.getElementById('list');
const clone = ul.cloneNode(true);
appendDataToElement(clone, data);
ul.parentNode.replaceChild(clone, ul);

[注] - 現代瀏覽器使用了隊列來存儲多次修改,因此上述的優化可能效果不是很理想.

3 - 【避免觸發同步布局事件】

// 栗子: 多次使用到 offsetWidth 屬性
function initP(){for(let i = 0; i< paragraph.length; i++){paragraph[i].style.width = box.offsetWidth + 'px'}
}

上述代碼每次循環,都會使瀏覽器強制刷新隊列(box.offsetWidth),造成多次回流和重繪.改進如下:

const width = box.offsetWidth;
function initP(){for(let i = 0; i < paragraph.length; i++){paragraph[i].style.width = width + 'px'}
}

4 - 【復雜動畫的優化】

對于復雜動畫效果,由于會經常的引起回流和重繪。因此,我們可以使用絕對定位,讓它脫離文檔流。否則會引起父元素以及后續元素頻繁的回流 - 栗子

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/250280.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/250280.shtml
英文地址,請注明出處:http://en.pswp.cn/news/250280.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

2017-2018 ACM-ICPC, Asia Daejeon Regional Contest

C 有n個節點和m邊條&#xff0c;求一條最長的路徑&#xff0c;該路徑(c1,c2,c3...cn)滿足 不出現重復的節點&#xff0c;ci 和ci1是鄰居節點&#xff0c;且 ci 的鄰居節點數量小于ci1的鄰居節點數量。 記憶DFS遍歷&#xff0c;每次遞歸計算的值都保存在數組里&#xff0c;這樣復…

javascript --- 將DOM結構轉換成虛擬DOM 虛擬DOM轉換成真實的DOM結構

虛擬DOM的實現 使用虛擬DOM的原因: 減少回流與重繪 將DOM結構轉換成對象保存到內存中 <img /> > { tag: img} 文本節點 > { tag: undefined, value: 文本節點 } <img title"1" class"c" /> > { tag: img, data: { title "1&q…

swap(a,b)值交換的4種方法

方法一&#xff1a;int tmp 0; tmp b;b a; a tmp; 方法二&#xff1a;a ab; b a-b; a a-b;方法三&#xff1a;a ^ b ^ a^ b;方法四&#xff1a;a ab-(ba);轉載于:https://www.cnblogs.com/vocaloid01/p/9514126.html

裝系統工具

安裝如果失敗,注意是不是工具的版本太老導致 系統分區工具: DiskGeniusPortable 刻錄工具: UlraISO rufus https://rufus.ie/ win32diskimager 轉載于:https://www.cnblogs.com/jiangfeilong/p/9937164.html

小程序WXML基本使用

數據綁定 <!--wxml--> <view> {{message}} </view> // page.js Page({data: {message: Hello MINA!} }) 列表渲染 <!--wxml--> <view wx:for"{{array}}"> {{item}} </view> // page.js Page({data: {array: [1, 2, 3, 4, 5]} })…

javascript --- vue中簡單的模板渲染

一層的渲染 將下面的模板中的mustache語法使用給定數據渲染. 模板如下 <div id"root"><div><div><p>{{name}} - {{message}}</p></div></div><p>{{name}}</p><p>{{msg}}</p> </div>數據如…

tomcat 虛擬路徑 與 虛擬主機配置

虛擬路徑配置 方法一&#xff1a;此方法需要重啟服務 打開下面文件 在host里面添加context標簽 <Context docBase"D:\test" path"/testServlet/aaaaa" reloadable"true" /> 瀏覽器訪問&#xff1a;http://172.16.6.103:1080/testServlet/a…

20172328 2018-2019《Java軟件結構與數據結構》第八周學習總結

20172328 2018-2019《Java軟件結構與數據結構》第八周學習總結 概述 Generalization 本周學習了二叉樹的另一種有序擴展&#xff1f;是什么呢&#xff1f;你猜對了&#xff01;ヾ(???)&#xff89;&#xff9e;就是堆。本章將講解堆的鏈表實現and數組實現&#xff0c;以及往…

javascript --- 函數的柯里化 Vue 2.x中柯里化的使用

函數式編程部分重點 參考資料: 函數式編程 柯里化 只傳遞給函數一部分參數來調用它,讓它返回一個函數去處理剩下的參數 var add function (x) {return function(y) {return x y} }var increment add(1) var addTen add(10)increment(2) // 3addTen(10) // 12判斷元素:V…

MYSQL重置ROOT密碼

背景 mysql 服務器長時間未使用&#xff0c;管理員當時設置的root 密碼忘記&#xff0c;需要重置 root 密碼&#xff0c;并加以妥善保存。 步驟 關閉 mysql 服務以跳過密碼驗證的方式啟動 mysql 服務mysqld --skip-grant-tables本地登陸后設置新的root 密碼 update mysql.user …

javascript --- Vue初始化 模板渲染

不帶響應式的Vue縮減實現 模板 現有模板如下: <div id "app"><div class"c1"><div titlett1 id"id">{{ name }}</div><div titlett2 >{{age}}</div><div>hello3</div></div><ul>…

#RANK_1 極其簡單的遞歸——騎士與金幣

2000:金幣 總時間限制: 1000ms內存限制: 65536kB描述國王將金幣作為工資&#xff0c;發放給忠誠的騎士。第一天&#xff0c;騎士收到一枚金幣&#xff1b;之后兩天&#xff08;第二天和第三天&#xff09;里&#xff0c;每天收到兩枚金幣&#xff1b;之后三天&#xff08;第四、…

動手動腦4

import java.io.*; public class ThrowMultiExceptionsDemo { public static void main(String[] args) { try { throwsTest(); } catch(IOException e) { System.out.println("捕捉異常"); }}private static void throwsTest() throws ArithmeticException,IOExcep…

javascript --- 對象原型

對象原型 參考 - MDN Javascript中的原型 在Javascript中,每一個函數都有一個特殊的屬性,叫做原型 下面獲取函數的原型fn.prototype function f1(){} console.log(f1.prototype) /*{constructor: f f1()__proto__:{constructor: f Object()__defineGetter__: f __defineGe…

從零認識單片機(9)

keil軟件&#xff1a; IDE:IDE是集成開發環境&#xff0c;就是用來開發的完整的軟件系統。 keil和mdk: keil:只能用來開發單片機 mdk:基于keil 拓展ARM的開發&#xff0c;主要用來開發ARM-cortex-m系列單片機的程序。 使用keil打開已有的工程項目&#xff1a; 1、IDE開發軟件&a…

javascript --- vue2.x中原型的使用(攔截數組方法) 響應式原理(部分)

說明 在Vue2.x中,利用了對原型鏈的理解,巧妙的利用JavaScript中的原型鏈,實現了數組的pop、push、shift、unshift、reverse、sort、splice等的攔截. 你可能需要的知識 參考 - MDN 原型鏈 JavaScript常被描述為一種基于原型的語言(prototype-based language),每個對象擁有一…

dubbo-admin構建報錯

dubbo-admin構建報錯 意思是maven庫里沒有dubbo2.5.4-SNAPSHOT.jar這個版本的dubbo的jar包&#xff0c;把dubbo-admin項目的pom.xml的   <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>${proje…

javascript --- 手寫Promise、快排、冒泡、單例模式+觀察者模式

手寫promise 一種異步的解決方案, 參考 Promise代碼基本結構 function Promise(executor){this.state pending;this.value undefined;this.reason undefined;function resolve(){}function reject(){} } module.exports Promisestate保存的是當前的狀態,在Promise狀態發…

PyCharm 通過Github和Git上管理代碼

1.Pycharm中設置如圖: 2.配置Git,通過網頁 https://www.git-scm.com/download/win 下載 3. 轉載于:https://www.cnblogs.com/0909/p/9956406.html

【BZOJ】2395: [Balkan 2011]Timeismoney

題解 最小乘積生成樹&#xff01; 我們把&#xff0c;x的總和和y的總和作為x坐標和y左邊&#xff0c;畫在坐標系上 我們選擇兩個初始點&#xff0c;一個是最靠近y軸的A&#xff0c;也就是x總和最小&#xff0c;一個是最靠近x軸的B&#xff0c;也就是y總和最小 連接兩條直線&…