浏览器的渲染原理是每一个前端开发工程师的必修课,网络上也有不少这方面的文章,但大多数只是告诉你浏览器表现就是这样的,并无一个直观的认识,让人读完就瞬间忘却。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>
复制代码
刷新页面,执行过程以下图动画所示:
从动画中能够发现,图片既不阻塞解析,也不阻塞渲染。
根据上面的实例,咱们获得以下几点重要的结论:
浏览器相对于开发者而言,如同黑盒,学习浏览器渲染方面的知识时,能够从浏览器源码或者浏览器提供的调试工具两方面进行学习,网上的一些文章总结最好动手实践下,这样印象会更深入。
参考
扫一扫 关注个人公众号【前端名狮】,更多精彩内容陪伴你!