node實例推導瀏覽器的渲染機制

前言

瀏覽器的渲染原理是每一個前端開發工程師的必修課,網絡上也有不少這方面的文章,但大多數只是告訴你瀏覽器表現就是這樣的,並無一個直觀的認識,讓人讀完就瞬間忘卻。javascript

因此小編決定經過node實踐來模擬瀏覽器渲染時的一些場景,對市面上的文章的一些觀點進行驗證,但願可以加深本身的認識。css

動手實踐以前,先了解下瀏覽器的渲染機制的一些基本知識,以下圖所示:html

DOM:Document Object Model,瀏覽器將HTML解析成樹形的數據結構,簡稱DOM。前端

CSSOM:CSS Object Model,瀏覽器將CSS代碼解析成樹形的數據結構java

Render Tree:DOM 和 CSSOM 合併後生成 Render Tree(Render Tree 和DOM同樣,以多叉樹的形式保存了每一個節點的css屬性、節點自己屬性、以及節點的孩子節點)node

Node實例推導segmentfault

爲了方便觀察靜態資源加載狀況和渲染細節,用node搭建一個靜態服務器,代碼以下:瀏覽器

const http = require('http');
const fs = require('fs');

let hostname = '127.0.0.1';
let port = 8080;

let server = http.createServer((req, res) => {
    console.log(req.url);
    if (req.url == '/a.js') {
        fs.readFile('src/a.js', (err, data) => {
            res.writeHead(200, {'Content-Type': 'text/javascript'});
            setTimeout(() => {
                res.write(data);
                res.end();
            }, 10000) // 延遲 10s 再返回 a.js 文件
        })
    } else if(req.url == '/b.js') {
        fs.readFile('src/b.js', (err, data) => {
            res.writeHead(200, {'Content-Type': 'text/javascript'});
            res.write(data);
            res.end();
        })
    } else if(req.url == '/index.html') {
        fs.readFile('src/index.html', (err, data) => {
            res.writeHead(200, {'Content-Type': 'text/html'});
            res.write(data);
            res.end();
        })
    } else if (req.url == '/style.css') {
        fs.readFile('src/style.css', (err, data) => {
            res.writeHead(200, {'Content-Type': 'text/css'});
            res.write(data);
            res.end();
        })
    } 
})

server.listen(port, hostname, () => {
    console.log(`server has already started: ${hostname}:${port}`)
})
複製代碼

從上面代碼中,咱們知道a.js的請求是延遲10s才響應返回的bash

啓動服務器,在瀏覽器打開 http://127.0.0.1:8080/index.html服務器


驗證問題一:外部靜態資源是如何請求的?

index.html 文件內容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>瀏覽器渲染原理</title>
    <script src='http://127.0.0.1:8080/a.js'></script>
    <link rel="stylesheet" href="http://127.0.0.1:8080/style.css">
</head>
<body>
    <p id='hh'>1111111</p>
    <script src='http://127.0.0.1:8080/b.js'></script>
    <p>222222</p>
    <p>3333333</p>
</body>
</html>
複製代碼

刷新頁面看下Timeline,以下圖所示:

從上面的動畫中咱們能獲得以下幾點:

  1. 頁面10s以後才展現,因此a.js阻塞了頁面的渲染展現;
  2. 第一次Parse HTML解析時,a.js形成了阻塞,預解析就去發起請求style.css,b.js,因此三個靜態資源看起來是一塊兒請求的,這一點須要看下後面的驗證;
  3. 三個靜態資源都是在解析html標籤時發起請求的,這一點也須要再驗證。

下面修改一下html內容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>瀏覽器渲染原理</title>
    <link rel="stylesheet" href="http://127.0.0.1:8080/style.css">
</head>
<body>
    <p id='hh'>1111111</p>
    <p>重複</p>
    <p>重複</p>
    ....
    ....重複5000行
    <script src='http://127.0.0.1:8080/a.js'></script>
    <script src='http://127.0.0.1:8080/b.js'></script>
    <p>222222</p>
    <p>3333333</p>
</body>
</html>
複製代碼

刷新頁面,會獲得以下TimeLine圖片:

從圖中咱們能夠得知:

  1. 當html內容太多的時候,瀏覽器須要分段接收,解析的時候也要分段解析;
  2. 靜態資源並非同時請求的,也不是解析到指定標籤的時候纔去請求的,瀏覽器會自行判斷,若是當前操做比較耗時,就會請求後面的資源,因此靜態資源請求的時機是沒法肯定的,瀏覽器有多重兼容處理方案。

驗證問題二:JS 對 HTML 的解析和渲染方面的影響

// index.html 文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>瀏覽器渲染原理</title>
    <link rel="stylesheet" href="http://127.0.0.1:8080/style.css">
</head>
<body>
    <p id='hh'>1111111</p>
    
    <script src='http://127.0.0.1:8080/b.js'></script>
    <script src='http://127.0.0.1:8080/a.js'></script>
    <p>222222</p>
    <p>3333333</p>
</body>
</html>
複製代碼

刷新頁面,執行過程以下圖動畫所示:

從執行過程當中咱們發現,因爲a.js的延遲返回,a.js沒有下載完成,Dom樹解析構建過程被阻塞中止,但a.js前面解析出來的html標籤被渲染展現出來了。當a.js下載完成後,繼續解析後面的標籤並渲染展現。固然,瀏覽器不是解析一個標籤就繪製顯示一次,當遇到阻塞或者比較耗時的操做的時候纔會先繪製一部分解析好的。

綜上可知,JS會阻塞頁面的解析和渲染,這也是JS文件常被放在頁面底部的緣由

驗證問題三:CSS 對頁面渲染解析的影響

修改服務器node代碼,將style.css延遲10s再返回

// style.css

p:nth-child(1) {
    color: red;
}

p:nth-child(2) {
    color: blue;
}

p:nth-child(3) {
    color: green;
}

複製代碼
// index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>瀏覽器渲染原理</title>
    <link rel="stylesheet" href="http://127.0.0.1:8080/style.css">
</head>
<body>
    <p id='hh'>1111111</p>
    <p>222222</p>
    <p>3333333</p>
</body>
</html>
複製代碼

刷新頁面,執行過程以下圖動畫所示:

從上面執行流程中發現,style.css 延遲10s後返回,頁面dom 樹被正常解析構建,可是沒有被渲染展現。當css下載完成後,頁面被被渲染而且樣式生效。

修改index.htmlstyle.css的位置,將其移到body最下方,代碼以下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>瀏覽器渲染原理</title>
</head>
<body>
    <p id='hh'>1111111</p>
    <p>222222</p>
    <p>3333333</p>
    <link rel="stylesheet" href="http://127.0.0.1:8080/style.css">
</body>
</html>
複製代碼

刷新頁面,執行過程以下圖動畫所示:

從動畫中發現,style.css的延遲加載,沒有阻塞前面的dom樹的解析構建和渲染,渲染的P元素沒有樣式。當style.css下載完成後,元素的樣式生效並展現。

綜上可知,CSS不阻塞dom樹的構建解析,只會阻塞其後面元素的渲染,不會阻塞其前面元素的渲染。

若是將CSS放到頁面底部,會先渲染出不帶樣式的頁面內容,等CSS加載樣式會生效,頁面看着會有抖動的現象,因此CSS通常放在head中。

驗證問題四:圖片對頁面渲染解析的影響

修改node代碼,對code.png作延時處理,具體代碼以下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>瀏覽器渲染原理</title>
    <link rel="stylesheet" href="http://127.0.0.1:8080/style.css">
</head>
<body>
    <p id='hh'>1111111</p>
    <img src="./code.png"/>
    <p>222222</p>
    <p>3333333</p>
</body>
</html>

複製代碼

刷新頁面,執行過程以下圖動畫所示:

從動畫中能夠發現,圖片既不阻塞解析,也不阻塞渲染。

總結

根據上面的實例,咱們獲得以下幾點重要的結論:

  1. 靜態資源並非同時請求的,也不是解析到指定標籤的時候纔去請求的,瀏覽器會自行判斷;
  2. JS 會阻塞頁面的解析和渲染,同時瀏覽器也存在預解析,遇到阻塞能夠繼續解析下面的元素;
  3. CSS`不阻塞dom樹的構建解析,只會阻塞其後面元素的渲染,不會阻塞其前面元素的渲染;
  4. 圖片既不阻塞解析,也不阻塞渲染。

瀏覽器相對於開發者而言,如同黑盒,學習瀏覽器渲染方面的知識時,能夠從瀏覽器源碼或者瀏覽器提供的調試工具兩方面進行學習,網上的一些文章總結最好動手實踐下,這樣印象會更深入。

參考

segmentfault.com/a/119000000…

掃一掃 關注個人公衆號【前端名獅】,更多精彩內容陪伴你!

相關文章
相關標籤/搜索