某天, 小枫在网上浏览前端性能优化的相干文章, 文章中指出: 将JavaScript的引入置于<body></body>尾部
小枫考虑了一会, 整个人都自闭了...于是乎, 接着上网查阅材料:
为什么JavaScript的引入要置于<body></body>尾部?
查了一圈之后发现, 网上广泛的观点如下:
JavaScript执行会阻塞HTML的渲染, 因而将JavaScript的引入置于<body></body>尾部, 能够使页面先出现进去, 防止JavaScript的执行导致页面白屏;
出于钻研的精力, 小枫决定本人来验证这个论断, 请给小枫点个赞谢谢(是字体本人加粗的, 逃0.0)
大家都晓得, chrome(文章应用的是版本为: 86.0.4240.80)是个神器, 自带性能调优工具, 能够记录剖析网页的渲染流程: 按"F12"或是"鼠标右键+查看"关上控制台, 抉择Performance栏, 如下图;
咱们先新建一个简略的HTML文件, 应用Performance剖析页面的渲染流程:
<!doctype html><html> <head> <title>钻研精力</title> </head> <body> <p>你好, 小枫</p> </body></html>
从上图能够看出, 网页的渲染流程大抵如下:
- Parse HTML 该阶段生成了DOM Tree和CSSOM Tree;
- Layout 将DOM Tree联合CSSOM Tree, 生成Layout Tree(又称Render Tree), 计算每个元素的尺寸和地位;
- Update Layout Tree 更新Layout Tree;
- Paint 生成PaintLayout Tree记录元素绘制程序;
- Composite 合成视图输入到屏幕;
简略了解网页渲染流程之后, 咱们来对问题进行验证, 列举以下两种状况:
- <script></script>置于<head></head>中:
<!doctype html><html> <head> <title>钻研精力</title> <script> for (let i = 0; i < 100000; i++) { for (let j = 0; j < 100000; j++) {} } </script> </head> <body> <p>你好, 小枫</p> </body></html>
在下面的html文件中, 咱们将<script></script>置于<head></head>中, <script></script>中是一段计算量很大的JavaScript, 值得注意的是, 这里的JavaScript是以嵌入的模式存在于HTML中, 并非应用src属性进行内部引入; 接着在浏览器关上该html文件, 并应用Performance进行剖析:
script标签置于head标签中
如上图所示, ParseHTML阶段始终呈阻塞状态, 直到Evalute Script阶段完结才从新开始ParseHTML, 接着进行Layout以及Paint等渲染页面流程; 在这个过程中页面始终出现白屏状态, 直到JavaScript加载实现才有内容展现; 能够看出, JavaScript的执行阻塞了HTML的解析和渲染;
- <script></script>置于<body></body>中:
<!doctype html><html> <head> <title>钻研精力</title> </head> <body> <p>你好, 小枫</p> <script> for (let i = 0; i < 100000; i++) { for (let j = 0; j < 100000; j++) {} } </script> </body></html>
接着, 咱们把<script></script>放在<body></body>尾部, 此处的JavaScript同样为一段计算量很大的代码, 并且也以嵌入的模式引入; 接着应用Performance对网页进行剖析:
script标签置于body标签尾部
如上图所示, 因为JavaScript的执行, ParseHTML阶段同样始终呈阻塞状态, 但不一样的是, 当Evalute Script阶段完结时, 间接进行Layout和Paint等渲染网页流程; 这个过程页面同样始终白屏, 直到JavaScript执行实现; 咱们能够这么了解: HTML自上往下执行, 因而在执行JavaScript之前ParseHTML流程失常执行: 当script标签放在body标签尾部时, 浏览器会先解析HTML, 之后遇到script标签, 开始执行JavaScript, 这个过程将会阻塞HTML的解析渲染, 所以页面才会始终出现白屏状态直到JavaScript执行结束;
从下面两种状况能够看出, 当JavaScript以script标签嵌入的形式执行时, 不论script标签置于head标签中或是置于body标签尾部, 都会因为JavaScript阻塞了HTML解析渲染的起因, 导致页面白屏, 直到JavaScript加载结束;
所以这能够证实网上搜寻到的观点是谬误的吗?
"不行!"
因为JavaScript的引入形式并非这一种, 还有另外一种更为常见的模式: 应用script标签的src属性引入内部JavaScript;于是乎, 以script标签引入内部JavaScript的模式, 开启下一轮验证:
- <script></script>置于<head></head>中:
将计算量很大的代码移至js文件: index.js
for (let i = 0; i < 100000; i++) { for (let j = 0; j < 100000; j++) {}}
并在html中引入 index.js:
<!doctype html><html> <head> <title>钻研精力</title> <script src="./index.js"></script> </head> <body> <p>你好, 小枫</p> </body></html>
接着, 应用Performance对网页进行剖析:
如上图, 浏览器先调用Network线程下载HTML文件, 之后对HTML进行自上往下解析工作, 直到遇到<head></head>中的script标签, 因为script标签引入的是内部js文件, 浏览器接着调用Network线程下载指标js文件, 能够看到, 这一过程中并没有进行ParseHTML;
下载完js文件之后, 浏览器开始执行JavaScript, 期待代码执行结束之后, 开始ParseHTML, Layout以及Paint等网页解析渲染操作; 整个流程, 网页始终出现白屏状态, 直到JavaScript执行结束, 也就是说, 当内部引入js文件时, script标签置于head标签中时, Network线程下载js文件将会阻塞HTML的解析, 导致页面在JavaScript下载并执行完之前始终出现白屏状态;
- <script></script>置于<body></body>中:
将script标签移到body尾部:
<!doctype html><html> <head> <title>钻研精力</title> </head> <body> <p>你好, 小枫</p> <script src="./index.js"></script> </body></html>
接着应用Performance进行网页剖析:
如图所示, HTML自上而下解析, 进行ParseHTML, 遇到引入内部js的script标签, Network线程开始下载js文件, 网页的渲染同时进行, 直到js文件下载实现, 开始执行JavaScript; 整个过程中, 网页在JavaScript执行结束前先呈现出内容, 因为在js文件下载之前, 浏览器曾经实现HTML的解析工作, 而在js文件下载过程中, 网页的渲染并没有因而阻塞, 能够得出结论:
"当应用script标签引入内部js文件时, js文件下载并不阻塞网页的渲染, 而是阻塞HTML的解析!"
为了充沛验证上述论断, 咱们在script标签后加多一个元素:
<!doctype html><html> <head> <title>钻研精力</title> </head> <body> <p>你好, 小枫</p> <script src="./index.js"></script> <p>你好, 小枫</p> </body></html>
察看页面变动并应用Performance剖析网页:
如上图, 网页先展现出第一行文本, 第二行文本并未呈现, 此时script标签仍出于下载内部js文件阶段, 说Network线程下载js文件的过程阻塞第二行文本的解析, 而第一行文本能失常出现进去, 阐明这一过程并没有阻塞网页的渲染;
一段时间过后, JavaScript下载并执行结束, 此时第二行文字也显示进去;
总结
联合下面的验证, 能够得出结论:
- JavaScript的执行会阻塞HTML的解析渲染;
- 当应用script标签引入内部js文件时, Network线程会阻塞HTML的解析, 但不会阻塞HTML的渲染;
因而网上大部分的观点是谬误的, JavaScript执行的确会阻塞HTML的解析渲染, 若是以嵌入的形式引入JavaScript, 不论script标签是放在head标签中或是body标签尾部, 页面都会因为JavaScript的执行而继续白屏; 而在引入内部js文件的状况, 因为Network线程下载内部js文件仅阻塞HTML的解析而不会阻塞HTML的渲染, script标签置于body标签尾部能够防止因为js文件下载工夫太长导致的页面继续白屏!