参考: https://juejin.im/post/6844903497599549453#heading-6css
前端页面开发时,习惯于css文件置于头部head中,js文件置于body的底部。这样作的缘由是css不会阻塞dom的解析,js会阻塞dom的解析。html
css不影响dom的解析,那是否影响dom渲染?js经过增长defer或async属性,会对文件的加载执行有什么影响?(以chrome,node,为测试环境)前端
浏览器对dom的处理分为两步,首先是dom的解析(分段解析,解析出一部分,就渲染一部分),而后再是dom树的渲染,渲染的过程是须要css树的参与的(若是有的话);浏览器对html文件的解析自上而下进行,解析过程结束,即开始准备进行渲染。node
样式的变化会引发页面的重排和重绘,若是在明知有css文件正在下载的状况下,进行渲染,css加载完成后,会再次的进行渲染,至关于前次的渲染工做是无用功,因此浏览器为了不无用工做,会有优化动做:chrome
- 等待下载中的css文件完成,再去渲染。
- js执行前,会渲染一次已经解析的dom,保证js获取的dom是最新的。
有了上面的基础后,咱们就能够作出分析了。浏览器
1.css位置框架
a.css文件在dom前(body上)dom
结论:等待css加载完成,进行渲染异步
b.css文件在dom中(body中)async
结论: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模式没有影响
- defer阻塞DOMContentLoaded;async阻塞Load事件
5. css + js
a. css在前,js在后(不管在什么位置)
结论:css存在阻塞。js执行前,须要渲染一次,渲染dom须要等待以解析的link加载完成。
b. js在前,css在后(不管在什么位置)
结论:js优先解析和执行,css彻底不影响。
总结:1.css文件应该尽早加载,避免阻塞js的执行,因此应放在head中
2.js会阻塞后面的dom的解析和渲染,因此应放在尾部,或者添加defer/async 放在头部