javascript --- [讀書筆記] 回流與重繪 前端優化小結

1. 瀏覽器渲染原理

請說出: 從用戶在瀏覽器地址輸入網址,到看整個頁面,中間都發生了哪些事情?

  • HTTP請求階段
  • HTTP響應階段
  • 瀏覽器渲染階段

1.1 可能用到的知識

1.1.1 進程 Process、線程 Thread、 棧內存 Stack

  • 進程: 就是開的每一個程序: QQ、網易云音樂、Typora、VSCode…

  • 線程: 一個做的好的事情.

  • 棧內存: 用來提供一個環境,供我們執行代碼

1.1.2 多任務

現代操作系統比如Mac OS X, UNIX, Linux, Windows等,都是支持"多任務"的操作系統

單核CPU執行多任務: 操作系統輪流讓各個任務交替執行,任務1執行0.01秒,切換到任務二,任務2執行0.01秒,再切換到任務3…由于CPU的執行速度很快,我們感覺就像所有任務都再同時執行一樣

多核CPU執行多任務: 真正的并行執行多任務只能在多核CPU上實現,但是,由于任務數量遠遠多于CPU的核心數量,所以,操作系統也會自動把很多任務輪流調度到每個核心上執行

有些進程不止同時干一件事情,就需要同時運行多個"子任務",我們把進程內的這些子任務稱為線程.

多個線程可以同時執行,多線程的執行方式和多進程一樣的,也是由操作系統在多個線程之間快速切換,讓每個線程都短暫地交替運行,看起來就像同時執行一樣

1.2 瀏覽器渲染原理

  • 在服務器上有程序員提前寫的項目代碼.
  • 它存放在服務器的磁盤中,標識符為 project
  • request請求階段:客戶端在瀏覽器輸入網址的時候,瀏覽器會向服務器端發送請求(DNS解析、TCP的三次握手與四次揮手、HTTPS與HTTP的區別)
  • response響應階段 :瀏覽器有個專門的端口監聽這個請求,驗證之后,將項目的源代碼返回給客戶端瀏覽器(HTTP狀態碼、304緩存、HTTP報文)
  • 客戶端瀏覽器,拿到response響應的代碼后,專門在內存中開辟一個棧內存,給代碼的執行提供環境;同時分配一個主線程,去:
  1. 一行一行的解析核執行代碼.
<!-- 棧內存 -->
進棧 --->  <!DOCTYPE html>  ---> 執行完出棧
  1. 遇到 link、script、img、a、video等 標簽,主線程,開新建一個子線程去執行相應的內容.自己繼續向下執行,同時會將該異步任務放到任務隊列中
<!-- 棧內存 -->
進棧 ---> <link href="1.css"> ---> 會將異步任務放到 TaskQueue中
<!-- 任務隊列 -->
任務1: 請求1.css
  1. 根據上述規則: 同步代碼依次執行,遇到請求資源等異步代碼,會創建一個新的子線程,去執行,并將任務放到任務隊列(TaskQueue)中,然后繼續向下執行…
  2. 因此主線程相當于是一直在執行同步任務.速度會很快
  3. 很快,主線程執行到頁面底部
<!-- 棧內存 -->
進棧 ---> </html> ---> 出棧
<!-- 任務隊列 -->
任務1: 請求1.css(未完成)
任務2: 請求2.css(未完成)
...
任務n: 請求...(未完成)
  1. 當執行完畢 </html>僅僅只在內存中生產了一個DOM樹(注意,此時的異步任務還沒有執行)

  2. 事件循環(Event Loop):生成DOM樹之后,主線程會在任務隊列(Task Queue)中去尋找已經準備好的異步任務,將其取出到棧內存中執行

<!-- 任務隊列 -->
任務1: 請求1.css(未完成)
任務2: 請求2.css(已完成) ---> 取出 ---> [棧內存]
...
任務n: 請求n(已完成)<!-- 棧內存 -->
進棧 ---> 任務2 ---> 
  1. 步驟7的機制就是 事件循環(Event Loop), 當任務隊列中的最后一條css獲取成功且在棧內存中執行完畢之后,會在內存中生產一個CSSOM.然后瀏覽器會把 CSSOM樹核DOM樹結合在一起生成一棵 Render Tree

  2. 得到渲染樹(Render Tree)之后,進行回流(Layout): 根據生成的渲染樹,計算它們在設備視口(view port)內的確切位置和大小,這個計算階段稱為回流,之后就是重繪(Painting)

  3. Painting(重繪): 根據渲染樹以及回流得到的幾何信息,得到節點的絕對像素.

  4. 絕對像素被分到一個圖層中,每個圖層又會被加載到GPU中形成渲染紋理,最終渲染到頁面上

[注] : 圖層在GPU中, transform是不會觸發repaint的,使用transform的圖層都會由 獨立的合成進程進行處理

1.2.1 性能優化

  • (減少HTTP請求次數和數據量大小) : 資源合并與壓縮 (圖片、樣式、JS文件)
  • 圖片懶加載: 第一次回流重繪的時候,不加載圖片,當第一次渲染完成之后,當屏幕滾動到哪里,就加載對應位置的數據

1.2.2 優化點

  • request階段: DNS解析、TCP的三次握手與四次揮手、HTTPS與HTTP的區別
  • response階段: HTTP狀態碼、304緩存、HTTP報文

1.2.2.1 DNS預解析(Chrome瀏覽器完成)

當用戶將鼠標停留在一個鏈接上,就預示著一個用戶的偏好以及下一步的瀏覽行為.

  • 這時,Chrome就可以提前進行DNS Lookup及TCP握手
  • 當在地址欄觸發高可能性選項時,同樣會觸發DNS lookup 和TCP預連接
  • Chrom會研究,每個人每天可能訪問的網址,并對網頁上的子資源嘗試預解析、預加載以提高用戶體驗

1.3 性能優化

  • DOM的重繪和回流 Repaint & Reflow

  • 重繪: 元素樣式的改變 (但寬度、大小、位置等不變)

如 outline,visibility,color,background-color等

  • 回流: 元素的大小或者位置發生了變化(當頁面布局和集合信息發生變化的時候),觸發了重新布局,導致渲染樹,重新計算布局和渲染.

如添加或刪除可見的DOM元素;元素的位置發生變化;元素的尺寸發生變化;內容發生變化(比如文本變化或圖片被另一個不同尺寸的圖片所代替);頁面一開始渲染的時候(這個無法避免);因為回流是根據視口的大小來計算元素的位置和大小的,所以瀏覽器的窗口尺寸變化也會引發回流

注意: 回流一定會觸發重繪,而重繪不一定會回流

  • 進可能減少 回流和重繪: 因為回流會根據當前窗口的大小計算元素的位置,然后回觸發重繪.根據渲染樹和回流的幾何位置信息,算出元素的絕對像素,進而通知GPU重新渲染.

  • 避免DOM的回流: mvvm / mvc / virtual dom / dom diff

1.3.1 分離讀寫操作 (現代的瀏覽器都有渲染隊列的機制)

? offsetTop、 offsetLeft 、 offsetWidth、 offsetHeight、 clientTop、 clientLeft、 clientWidth、clientHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、getComputedStyle、currentStyle

<style>#box {width: 100px;height: 100px;background: red;border: 10px solid green;}
</style>
<body><div id="box"></div><script>let box = document.getElementById('box');box.style.width = '200px';box.style.height = '200px';box.style.margin = '10px';</script>
</body>
  • 當代瀏覽器,遇到了上面連續對DOM操作的代碼,會將其先存在一個隊列里面,然后一起進行一次回流.
  • 但如果遇到如下
<script>let box = document.getElementById('box');box.style.width = '200px';console.log(box.clientWidth);box.style.height = '200px';box.style.margin = '10px';
</script>
  • 以上代碼會回流2次,因為在寫操作中穿插了讀操作,將讀操作寫在最下面可以使回流變成一次
<script>let box = document.getElementById('box');box.style.width = '200px';box.style.height = '200px';box.style.margin = '10px';console.log(box.clientWidth);
</script>

1.3.2樣式集中改變

display.cssText = ‘width:20px; height:20px;’

divclassName = “box”

  • 不把樣式分開寫,統一寫

  • 使用cssText

<script>let box = document.getElementById('box');box.style.cssText = 'width:200px;height:200px;margin:10px';
</script>
  • 使用添加類
<style>.box{width: 200px;height: 200px;margin: 10px;}
</style>
<script>let box = document.getElementById('box');box.className = 'box'
</script>

1.3.3 緩存布局信息

  • 看下面代碼
<script>box.style.width = box.clientWidth + 10 + 'px';box.style.height = box.clientHeight + 10 + 'px'
</script>
  • 以上會觸發2次回流,因為在遇到clientWidth、clientHeight時,瀏覽器都會清空渲染隊列,實現一次回流

  • 改進做法:

<script>let width = box.clientWidth + 10 + 'px';let height = box.clientHeight + 10 + 'px';box.style.width = width;box.style.height = height;
</script>

1.3.4 元素批量修改

  • 例如我們經常動態的往ul中添加li屬性, 你可能會寫出如下代碼
<ul id="box"></ul>
<script>let box = document.getElementById('box');for(let i =0; i< 5; i++){let li = document.createElement('li');li.innerHTML = i;box.appendChild(li);}
</script>
  • 以上會觸發5次回流,可以將DOM操作,次數太少了看不出效果…我們嘗試觸發50000次回流,打開網頁.你會發現瀏覽器卡頓了一下,然后渲染.

  • 嘗試使用字符串拼接的方式來減少回流次數

<ul id="strBox"></ul>
<script>let strBox = document.getElement('strBox');let str = '';for(let i =0; i< 50000; i++){str += `<li>${i}</li>`;}strBox.innerHTML = str;
</script>
  • 在創建50000個li的情況下, 使用第一種方式大概花費330ms,而第二種方式只需100ms

1.3.5 硬件加速優化

  • CSS3硬件加速

[栗子] : 將盒子向右移動100像素

<script>box.style.left = '100px';
</script>

使用上面技術,會使頁面產生一次回流

但是使用transform則不會產生回流

<script>box.style.transform = 'translateX(200)'
</script>
  • 你可能會好奇,為什么使用CSS3的API,不會產生回流,CSS3硬件加速的工作原理

  • 不會引起回流重繪的屬性: transform \ opacity \ filters

  • 硬件加速可能存在的缺點: 過多使用會占用大量內存,性能消耗可能會比較嚴重、有時候會導致字體模糊等…

1.3.6 犧牲平滑度換取速度

  • 改變動畫的最小平移單位,例如: 將原本需要1像素移動改為3像素…這樣可以減少DOM的節流和重繪

1.4 小結

  • 從用戶在瀏覽器地址輸入網址,到看整個頁面,中間都發生了哪些事情?
  1. 首先瀏覽器會根據輸入的域名,通過域名服務器得ip地址,
  2. 然后會檢查本地中是否有請求的資源,并且判斷請求資源是否是最新的,如果是則返回,否則去服務器端得到資源(如果有錯誤,希望指出)
  3. 得到資源后,瀏覽器會在內存中開辟一個棧內存,同時分配一個主線程去從上到下解析文檔結構,遇到 link、img等標簽會將其放在任務隊列中,繼續向下執行
  4. 執行到最底部,此時生成了一個 DOM 樹,然后進入事件循環(Event Loop)
  5. 事件循環: 瀏覽器從 任務隊列(Task Queue)中取出已經準備好的的任務到棧內存中逐條執行
  6. 結束后會生成一棵CSSOM樹, 然后主線程會根據 DOM樹和 CSSOM樹生成一棵渲染樹(Render Tree)
  7. 之后會根據 視口大小(View Port)計算出節點的幾何位置(稱為節流)
  8. 之后是重繪: 根據渲染樹和 節流算出的幾何位置得到節點的確切位置.
  9. 完成重繪后,主線程會將每個每個絕對位置加載到GPU中,最終渲染成紋理,呈現在頁面上.

參考

Chrome高性能的秘密:DNS預解析

CSS3硬件加速的工作原理

瀏覽器渲染原理

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

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

相關文章

ARP協議,以及ARP欺騙

1.定義&#xff1a; 地址解析協議&#xff0c;即ARP&#xff08;Address Resolution Protocol&#xff09;&#xff0c;是根據IP地址獲取物理地址的一個TCP/IP協議。主機發送信息時將包含目標IP地址的ARP請求廣播到網絡上的所有主機&#xff0c;并接收返回消息&#xff0c;以此…

css --- [小結]讓盒子水平垂直居中的解決方案

描述 有如下模型,想辦法讓 <style>.box{width: 500px;height: 500px;background: skyblue;} </style> <div class"box"><div class"inner"></div> </div>想辦法讓inner在box中水平垂直居中 方案1: 使用絕對定位 讓…

數組洗牌 Fisher Yates

看播放器代碼時發現的這個洗牌算法&#xff0c;再網上查了一番 作用是把數組變成隨機序列&#xff0c;原理類似于從牌堆A中隨機抽牌放進牌堆B 代碼1&#xff1a; 返回一個由&#xff08;數組下標&#xff09;組成的數組 function random(length) {function shuffle (arr) {for…

一個不錯的MYSQL數據庫備份類,PHP版,一個文件,精簡版

1 <?php2 class DbManage {3 var $db; // 數據庫連接4 var $database; // 所用數據庫5 var $sqldir; // 數據庫備份文件夾6 // 換行符7 private $ds "\n";8 // 存儲SQL的變量9 public $sqlContent "";10 // 每條sql…

javascript --- 堆棧內存與閉包的作用

你可能會用到的 堆內存: 存儲引用類型值所在的空間棧內存: 存儲基本類型值和存儲代碼所在空間函數上下文: JS每一個函數在執行的時候都會創建一個執行上下文 1. 堆內存中的數字和字符串都是相等的 let a {}, b0, c0; a[b] marron; a[c] Mar console.log(a[b]) // Mar第一…

python_sting字符串的方法及注釋

string類型是python內置的類型&#xff0c;無需安裝 方法/屬性說明 capitalize() 把字符串的第一個字符改為大寫 casefold() 把整個字符串的所有字符改為小寫 center(width) 將字符串居中&#xff0c;并使用空格填充至長度width的新字符串 count(sub[,start[,end]]) …

作業3

import turtle turtle.bgcolor(red) turtle.color(yellow)turtle.fillcolor(yellow) turtle.begin_fill() for i in range(5):turtle.forward(100)turtle.right(144) turtle.end_fill() turtle.done()轉載于:https://www.cnblogs.com/zhangkef97/p/9016608.html

javascript --- [小練習]變量提升、優先級綜合

求下列函數輸出結果 function Foo() {getName function() {console.log(1)}return this } Foo.getName function() {console.log(2) } Foo.prototype.getName function() {console.log(3) } var getName function() {console.log(4) }function getName() {console.log(5) …

Confluence 6 自定義 Decorator 模板的宏和針對高級用戶

宏 頁面的某些部分使用的是 Velocity 宏進行創建的&#xff0c;包括導航欄。有關宏的創建&#xff0c;你可以參考頁面 Working With Decorator Macros 頁面中的內容。 針對高級用戶 velocity 目錄是 Confluence 首先進行模板搜索的查找路徑。你可以對 Confluence 的 velocity …

Matlab之rand(), randn(), randi()函數的使用方法

1. rand()函數用于生成取值在&#xff08;0~1&#xff09;之間均勻分布的偽隨機數。rand(n)&#xff1a;生成n*n的0~1之間的滿足均勻分布的偽隨機矩陣&#xff1b;rand(m,n)&#xff1a;生成m*n的偽隨機數&#xff1b;rand(m,n,double)&#xff1a;生成m*n的雙精度偽隨機數&am…

javascript --- [有趣的條件]雙等號的隱式調用和數據劫持

1 雙等號的隱式調用和數據劫持 求下面條件,在a為什么樣時,等號成立 if(a 1 && a 2 && a 3){console.log(等號成立) }1.1 雙等號的隱式轉換 首先得了解雙等號的隱式轉換規則 等式備注對象 字符串隱式調用 toString方法將對象轉換成字符串null undeifne…

分組查詢

1、簡單分組查詢 語法形式&#xff1a; select function() from table_name where condition group by field; 進行分組查詢時&#xff0c;分組所依據的字段上的值一定要有重復值&#xff0c;否則分組沒有任何意義。 2、實現統計功能分組查詢 關鍵字group by單獨使用時&#xf…

深入探討多模態模型和計算機視覺

近年來&#xff0c;機器學習領域在從圖像識別到自然語言處理的不同問題類型上取得了顯著進展。然而&#xff0c;這些模型中的大多數都對來自單一模態的數據進行操作&#xff0c;例如圖像、文本或語音。相比之下&#xff0c;現實世界的數據通常來自多種模態&#xff0c;例如圖像…

移動硬盤函數不正確要如何尋回資料

移動磁盤打不開函數不正確&#xff0c;是因為這個I盤的文件系統內部結構損壞導致的。要恢復里面的數據就必須要注意&#xff0c;這個盤不能格式化&#xff0c;否則數據會進一步損壞。具體的恢復方法看正文 工具/軟件&#xff1a;AuroraDataRecovery 步驟1&#xff1a;先百度搜索…

vue --- [全家桶]vue-router

1. Vue - router Vue Router是 Vue.js 官方的路由管理器它和Vue.js的核心深度集成,可以非常方便的用于SPA應用程序的開發 Vue Router包含的功能有: 支持HTML5歷史模式或hash模式支持嵌套路由支持路由參數支持編程式路由支持命名路由 <div id"app"><rout…

HashMap的四種訪問方式

第一種&#xff1a;通過Map.entrySet使用iterator遍歷key和value 1 public void visit_1(HashMap<String,Integer> hm){ 2 Iterator<Map.Entry<String,Integer>> it hm.entrySet().iterator(); 3 while(it.hasNext()){ 4 Map.Entry<String ,Integer> …

16.unix網絡編程一卷 unp.h

unix網絡編程 --ubuntu下建立編譯環境 1.安裝編譯器&#xff0c;安裝build-essential sudo apt-get install build-essential 2.下載本書的頭文件 下載unp13e&#xff1a; http://pix.cs.olemiss.edu/csci561/prg561.1.html 3.進入unp13e 查看readme&#xff0c;照下列提示操作…

webpack --- [讀書筆記] webpack中常用的一些配置項

1. Webpack 當前Web開發面臨的困境 文件依賴關系錯綜復雜靜態資源請求效率低模塊化支持不友好瀏覽器對高級JavaScript特性兼容程度低 1.1 webpack概述 webpack是一個流行的前端項目構建工具,可以解決當前web開發中所面臨的困境. webpack提供了友好的模塊化支持,以及代碼壓…

spring中bean的作用域屬性single與prototype的區別

https://blog.csdn.net/linwei_1029/article/details/18408363 轉載于:https://www.cnblogs.com/stanljj/p/9907444.html

windows程序設計.第一個windos程序

Windows程序設計&#xff08;第5版&#xff09; windows程序需要調用API。 第一個Windows程序 1 /*HelloMsg.c -- Displays "Hello World!" in a message box*/ 2 #include <Windows.h> 3 4 int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE…