瀏覽器的渲染原理是每一個前端開發工程師的必修課,網絡上也有不少這方面的文章,但大多數只是告訴你瀏覽器表現就是這樣的,並無一個直觀的認識,讓人讀完就瞬間忘卻。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,以下圖所示:
從上面的動畫中咱們能獲得以下幾點:
a.js
阻塞了頁面的渲染展現;Parse HTML
解析時,a.js
形成了阻塞,預解析就去發起請求style.css,b.js
,因此三個靜態資源看起來是一塊兒請求的,這一點須要看下後面的驗證;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圖片:
從圖中咱們能夠得知:
// 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文件常被放在頁面底部的緣由
修改服務器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.html
中style.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>
複製代碼
刷新頁面,執行過程以下圖動畫所示:
從動畫中能夠發現,圖片既不阻塞解析,也不阻塞渲染。
根據上面的實例,咱們獲得以下幾點重要的結論:
瀏覽器相對於開發者而言,如同黑盒,學習瀏覽器渲染方面的知識時,能夠從瀏覽器源碼或者瀏覽器提供的調試工具兩方面進行學習,網上的一些文章總結最好動手實踐下,這樣印象會更深入。
參考
掃一掃 關注個人公衆號【前端名獅】,更多精彩內容陪伴你!