參考: 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 放在頭部