参考: https://juejin.im/post/6844903497599549453#heading-6
前端页面开发时,习惯于css文件置于头部head中,js文件置于body的底部。这样做的原因是css不会阻塞dom的解析,js会阻塞dom的解析。
css不影响dom的解析,那是否影响dom渲染?js通过增加defer或async属性,会对文件的加载执行有什么影响?(以chrome,node,为测试环境)
浏览器对dom的处理分为两步,首先是dom的解析(分段解析,解析出一部分,就渲染一部分),然后再是dom树的渲染,渲染的过程是需要css树的参与的(如果有的话);浏览器对html文件的解析自上而下进行,解析过程结束,即开始准备进行渲染。
样式的变化会引起页面的重排和重绘,如果在明知有css文件正在下载的情况下,进行渲染,css加载完成后,会再次的进行渲染,相当于前次的渲染工作是无用功,所以浏览器为了避免无用工作,会有优化动作:
等待下载中的css文件完成,再去渲染。
js执行前,会渲染一次已经解析的dom,保证js获取的dom是最新的。
有了上面的基础后,我们就可以做出分析了。
1.css位置
a.css文件在dom前(body上)
结论:等待css加载完成,进行渲染
b.css文件在dom中(body中)
结论:link前的dom首次无样式渲染,css加载完成,所有dom进行有样式的渲染
c.css文件在dom后(body下)
结论:首次无样式渲染,css加载完成,所有dom进行有样式的渲染
总之,在渲染前被解析到的css文件将阻塞dom的渲染。
2.js位置
a.js文件在dom前(body上)
结论:等待js加载并执行完成,进行渲染。(console.log(dom)为null)
b.js文件在dom中(body中)
结论:script前的dom首次渲染,js加载并执行完成,后面的dom进行渲染。(console.log(dom)只有script前的dom)
c.js文件在dom下(body下)
结论:不影响dom的渲染。(console.log(dom)获取所有dom)
总之,这个不多说(不考虑defer,async),任何位置的js文件对它之后的dom的解析和渲染都会阻塞。
3.js + defer(推迟)
这个布尔属性被设定用来通知浏览器该脚本将在文档完成解析后,触发 DOMContentLoaded 事件前执行。
(DOMContentLoaded事件在初始HTML文档完全加载和解析之后触发,而无需等待样式表、图像和子框架完成加载)
所以加了这个属性的scritp,将不会阻塞dom的解析(没说不阻塞渲染);但仍会等待它之前的css文件的下载
4. js+ async(异步)
对于普通脚本,该属性能够消除解析阻塞的 Javascript;那么普通脚本会被并行请求,并尽快解析和执行。
只有js时,defer和async的效果差不多,执行时间没有差别,可以获取dom,但img可能还未加载完成。
当存在css时,defer模式需要等待css的加载,再触发DOMContentLoaded事件 css会阻塞渲染。
当存在css时,async模式没有影响,因为async只和它本身有关,加载完成即执行。
5. css + js
a. css在前,js在后(无论在什么位置)
结论:css存在阻塞。js执行前,需要渲染一次,渲染dom需要等待以解析的link加载完成。
b. js在前,css在后(无论在什么位置)
结论:js优先解析和执行,css完全不影响。
总结:1.css文件应该尽早加载,避免阻塞js的执行,所以应放在head中
2.js会阻塞后面的dom的解析和渲染,所以应放在尾部,或者添加defer/async 放在头部
来源:oschina
链接:https://my.oschina.net/u/3272730/blog/4459672