这个涉及到浏览器渲染原理的一些知识了。
当构建DOM树时,若遇到<script>
标签,则会立刻停止DOM树的构建,而选择将<script>
内的脚本运行完后再构建DOM
所以一般的<script>
标签放在body
后面。
如果碰到外联的<script>
标签。DOM
的构建仍然被暂停以等待JavaScript脚本运行完,而且还需要将从服务器获得外联脚本的时间计算在内。
10 回答11.2k 阅读
5 回答4.8k 阅读✓ 已解决
4 回答3.1k 阅读✓ 已解决
2 回答2.7k 阅读✓ 已解决
3 回答1.5k 阅读✓ 已解决
3 回答2.3k 阅读✓ 已解决
3 回答2.2k 阅读✓ 已解决
如果你说的阻塞是阻塞解析html和阻塞渲染,那script无论是不是外链都会阻塞。如果说的是第一种情况,performance里会出现这个:





第一种情况的加载过程中,先绘制了第一段文字,再绘制第二段文字,第二种情况不会。
个人感觉出现这种情况是因为代码直接写在script时,解析到script后parse HTML和渲染都被阻塞(图里前面很长一段黄色的长条),script的内容被解析完后,会先把script前面的内容渲染出来,再去解析script后面的内容,css树解析完了,DOM树也经历了重绘重排,所以浏览器直接判定差不多可以开始首次绘制了,图里First Paint的时候,script后面的内容还没经历重绘重排,所以FP时没被绘制上去,也是因为没绘制完,所以FP后面还有个FMP(first meaningful paint),script后面的内容经历重绘重排后才会被渲染到页面上:
第二种情况:
阻塞解析渲染的script文件处理完后,直接开始解析script后面的内容,而不是先将前面的部分重绘重排,因此触发FP时,绘制的是页面所有内容
第二种情况首次绘制直接把所有内容绘制出来了,所以performance里找不到第一种情况里,前一段文字显示了,后面还没显示的帧。
但是实际上无论是不是外链,都阻塞了DOM解析和渲染(前面都有很长一段黄色长条)。
具体为什么第一种情况会先把script前面的部分重绘重排,第二种不会,个人感觉应该是因为第二种ui线程其实只需要解析很短的一段代码,因为script内容在外链里,下载交给网络请求线程,下载完直接让js线程解析,下载和解析时ui线程处于挂起状态,解析完之后ui线程被唤醒,继续完成之前的工作(解析html)。
但是第一种情况,script里耗时的内容在html里,解析的时候还是需要ui线程参与,ui线程判定是个很耗时的过程,所以js内容执行完直接就先把前面解析的重绘重排,再去解析后面的。
第一种情况,script黄条在parse HTML蓝条下面:
第二种情况,parse HTML蓝条和script黄条并列:
这个是个人观点,查了半天资料都没找到相关内容。。。
————————————————————————————————————————————————————————————————————————————
对比了一下第二种情况中,有时先出现hello,再出现world,有时hello和world一起出现,感觉有点眉目了。


hello和world一起出现的情况时,在ui线程对script前面的代码进行重绘重排之前,就接收到了外链的script代码:
先出现hello再出现world时,接收外链代码晚于重绘重排:
应该是和浏览器接收到内容的时间有关,超过一段时间没接收到外链的内容,就先进行重绘重排了,如果在这个时间内先接收到外链内容,就先开始解析外链JS代码