注:测试浏览器为chrome浏览器
我们先来看第一段代码:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>测试</title> </head> <body> <h1>Hello</h1> <script type="text/javascript"> let i = 1000000000 while(i>0){ i-- } </script> <h1>world</h1> </body> </html>
我们知道js会阻塞DOM解析和渲染,所以页面肯定会在内联script里的代码执行完成之后,再渲染出来
答案确实是这样
分析:一开始渲染进程的HTML 解析器开始解析DOM,当解析到内联script 脚本标签时,HTML 解析器会暂停解析DOM,此时JavaScript 引擎介入,并执行内联script 标签中的这段脚本,脚本执行完成之后,HTML 解析器恢复解析过程,继续解析DOM,然后进行后续的渲染,最终将页面上同时渲染出 Hello World
接下来,我们看第二段代码:
index.js:
var i = 1000000000 while(i>0){ i-- }
index.html:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>测试</title> </head> <body> <h1>Hello</h1> <script type="text/javascript" src="index.js"></script> <h1>world</h1> </body> </html>
那这段html代码的运行结果是不是和上面一样呢?
刚开始我以为是,后来发现并不是这样的,谷歌浏览器做了一些优化,所以执行结果会有一些变化
分析:当渲染引擎收到字节流之后,会开启一个预解析线程,用来分析 HTML 文件中包含的 JavaScript、CSS 等相关文件,解析到相关文件之后,预解析线程会提前下载这些文件。因为这段html代码里有外联script脚本,所以会丢到预解析线程去下载这个外联js文件,此时HTML 解析器开始解析DOM,当遇到外联script标签时,停止DOM解析,浏览器会渲染一次页面(当前的Hello会被渲染到页面上),然后执行下载完成的js文件,接着继续解析DOM,然后进行后续的渲染,最终将页面上就会渲染出 Hello World
注意:这里是先渲染出Hello ,然后过一会儿(这个期间在执行js),再渲染出World
答案:第一段代码和第二段代码的执行最终结果一样,但是渲染顺序不一样。第一段代码先间隔一段时间(执行js),然后Hello World会同时被渲染出来;第二段代码会先渲染出Hello,然后间隔一段时间(执行js),再渲染出World
总结:
虽然js都会阻塞DOM解析,但是浏览器对于内联script和外联script的渲染过程还是有一点点不同。内联js会阻塞DOM解析和渲染,直到js执行完成后,页面才会被渲染出来。外联js也会阻塞DOM解析和渲染,但是如果在外联script标签之前已经有DOM元素生成,则浏览器会优先渲染一次。我想这是因为浏览器不知道脚本的内容,因而碰到脚本时,只好先渲染页面,确保脚本能获取到最新的DOM
元素信息,尽管脚本可能不需要这些信息。
——个人理解,如有出错,请指正——