CSS和JS檔案的位置會影響頁面效率。 js腳本應放在底部,如果放在首部,當下載執行js時,會影響渲染行程繪製頁面;而CSS應放在頂部,如果放在底部,頁面可以逐步呈現,但在CSS下載並解析完畢後,已經呈現的文字和圖片就要需要依照新的樣式重繪。
js腳本檔案的位置
js腳本應該放在底部,原因在於js執行緒與GUI渲染線程是互斥的關係,如果js放在首部,當下載執行js的時候,會影響渲染行程繪製頁面,js的作用主要是處理交互,而交互必須得先讓頁面呈現才能進行,所以為了確保用戶體驗,盡量讓頁面先繪製出來。
CSS檔案的位置
CSS 是頁面渲染的關鍵因素之一,(當頁面存在外鏈CSS 時,)瀏覽器會等待全部的CSS 下載及解析完成後再渲染頁面。關鍵路徑上的任何延遲都會影響首屏時間,因而我們需要盡快將 CSS 傳輸到用戶的設備,否則,(在頁面渲染之前,)用戶只能看到一個空白的螢幕。
CSS檔案放在頂部一方面是因為放置順序決定了下載的優先級,更關鍵的是瀏覽器的渲染機制。
css在載入過程中不會影響到DOM樹的生成,但是會影響到Render樹的生成,進而影響到layout,所以一般來說,style的link標籤需要盡量放在head裡面,因為在解析DOM樹的時候是自上而下的,而css樣式又是透過非同步載入的,這樣的話,解析DOM樹下的body節點和載入css樣式能盡可能的並行,加快Render樹的生成的速度。
將CSS放在底部,頁面可以逐步呈現,但在CSS下載並解析完畢後,已經呈現的文字和圖片就要需要根據新的樣式重繪,這是一種不好的用戶體驗。
js、css等腳本位置對效能的影響
用一句話概括就是: JS 全阻塞,CSS 半阻塞。 (詞是我發明的,方便記憶而已)
JS 會阻塞後續 DOM 解析以及其它資源(如 CSS,JS 或圖片資源)的載入。
CSS 不會阻塞後續 DOM 結構的解析,不會阻塞其它資源(如圖片)的加載,但是會阻塞 JS 檔案的加載。
現代瀏覽器很聰明,會進行 prefetch 最佳化,瀏覽器在獲得 html 文件之後會對頁面上引用的資源進行提前下載。 (注意只是提前下載)
下面開始我就一邊測試,一邊解釋上述測試的結果:
測試的瀏覽器是Chrome,版本號為55.0.2883.95 (64-bit)
先用Nodejs 搭建一個簡單http 伺服器:
//test.jsconst http = require('http');const fs = require('fs');const hostname = '127.0.0.1';const port = 9000;http.createServer((req, res) => { if(req.url === "/") { fs.readFile("index.html", "utf-8", function(err, data) { res.writeHead(200, { 'Content-Type': 'text/html' }); res.write(data); res.end(); }) }else if(req.url === "/yellow.js") { //延迟 5s fs.readFile("yellow.js", "utf-8", function(err, data) { res.writeHead(200, { 'Content-Type': 'text/plain' }); setTimeout(function () { res.write(data); res.end(); }, 5000); }) }else if(req.url === "/blue.js") { //延迟 10s fs.readFile("blue.js", "utf-8", function(err, data) { res.writeHead(200, { 'Content-Type': 'text/plain' }); setTimeout(function () { res.write(data); res.end(); }, 10000); }) }else if(req.url === "/red.css") { //延迟 15s fs.readFile("red.css", "utf-8", function(err, data) { res.writeHead(200, { 'Content-Type': 'text/css' }); setTimeout(function () { res.write(data); res.end(); }, 15000); }) }else if(req.url === "/green.css") { //延迟 20s fs.readFile("green.css", "utf-8", function(err, data) { res.writeHead(200, { 'Content-Type': 'text/css' }); setTimeout(function () { res.write(data); res.end(); }, 20000); }) }}).listen(port, hostname, () => { console.log('Server running at ' + hostname);});
首頁的程式碼結構:
//index.html nbsp;html> <meta> <meta> <meta> <title>测试浏览器渲染</title> <p>First Line</p> <script></script> <p>Second Line</p> <link> <p>Third Line</p> <script></script> <p>Fourth Line</p> <link> <img alt="js和css檔案位置對頁面效能有什麼影響?" > <p>Fifth Line</p>
以及其它CSS和JS 檔案:
//yellow.js document.body.style.cssText = "background: yellow !important"; //blue.js document.body.style.cssText = "background: blue !important";
//red.css body { background:red !important; } //green.css body { background: green !important; }
說明下:yellow.js 和blue.js 下載時間分別為5s 和10s,red.css 和green.css 下載時間分別為15s 和20s。
之後將所有檔案放到同目錄下,在控制台輸入 node test.js,開啟瀏覽器存取 127.0.0.1:9000 就可以存取。
先來看第三點結論:現代瀏覽器很聰明,會進行 prefetch 最佳化,瀏覽器在取得 html 文件之後會對頁面上引用的資源進行提前下載。 (注意只是提前下載)
很好理解,從圖中可以看出:CSS、JS、圖片在瀏覽器拿到html 文件之後會將頁面上引用資源幾乎同時下載,但具體什麼時候執行要看html 的結構,注意我這裡使用的是Chrome 瀏覽器,其它瀏覽器可能會有差別。
還有就是一個奇怪的現象,Chrome 瀏覽器有時會對 img 進行 prefetch,有時則不會。
接著是第一點規則:
JS 會阻塞後續 DOM 解析以及其它資源(如 CSS,JS 或圖片資源)的載入。
從上圖可以看出,當瀏覽器解析到yellow.js 這行時候會等待yellow.js 加載,阻塞後續DOM 結構的解析(包括DOM 結構,其他所有資源(CSS, JS, 圖片))。
這個很好理解:
JS 運行在瀏覽器中,是單線程的,每個window 一個JS 線程,所以當然會阻塞後續DOM 樹的解析咯。
JS 有可能會修改 DOM 結構,為 DOM 添加樣式等等,所以這意味著在目前 JS 載入執行完成之前,後續資源的載入可能是沒有意義的。
其次是第二點:
CSS 不會阻塞後續 DOM 結構的解析,不會阻塞其它資源(如圖片)的加載,但是會阻塞 JS 檔案的加載。
這個就相對比較複雜點,讓我先上測試結果的圖:
從圖中可以得到以下總結:
在載入完yellow.js 後,當在下載red.css 時候並不會阻塞DOM 解析,並且由於第一點規則,當解析到blue.js 這行的時候,同樣會阻塞後續DOM 解析。
由於我們設定的red.css 下載時間為15s 而blue.js 為10s,而從前面第三條規則的圖中也可以看到,blue.js 在10s左右下載完而red.css 在15s 左右下載完畢。
最後在15s 時候頁面變為了藍色,這說明了CSS 阻塞了JS 的加載,後續的JS 文件雖然提前下載完畢了,但還是要等前面CSS 文件加載完後才能執行。
後續當blue.js 加載完之後可以看到,green.css 的下載並不會影響到後續img 的加載,所以說明CSS 檔案下載並不會影響後續圖片等其它資源以及DOM 的載入。
這個也好理解:JS 程式碼在執行前,瀏覽器必須保證在JS 之前的所有CSS 樣式都解析完成,不然不就亂套了,前面的CSS樣式可能會覆蓋JS 檔案中定義的元素樣式,這是CSS 阻塞後續JS 執行的根本原因。
最後這裡說明為什麼最後body 的背景色沒有變成綠色:因為js 定義的樣式在內聯樣式,優先權高於在CSS 檔案中定義的樣式,所以不是green.css 沒有加載,而是沒有生效。看下圖就知道了:(green 和red 樣式都被劃掉了)
所以知道了上述的結論之後,我們在開發的時候應該盡可能地:
將樣式或CSS 檔案定義在head 中,並且在處理此類請求的時候應該盡快能夠回應(CDN 什麼的),如果像上面請求一個CSS 檔案都要10s 的話,那你這頁估計沒多少人有耐心等下去。
將 JS 腳本檔案放在 body 底部,讓 DOM 結構能優先渲染出來,避免 DOM 被阻塞。
當編寫比較耗時的JS 程式碼時候盡可能使用非同步的方式進行加載,例如setTimeout, ajax 等等,同樣也是為了避免頁面渲染耗時過長,影響用戶體驗。
上面介紹了JS 會阻塞後續DOM 解析以及其它資源(如CSS,JS 或圖片資源)的加載,這是在沒有考慮到defer, async 的情況下。
當瀏覽器碰到script 腳本的時候:(不考慮瀏覽器的prefetch)
從使用的角度來看,首先把腳本丟到body 底部是比較好的最佳化選擇,此法可保證非腳本的其他一切元素能夠以最快的速度載入和解析。
上述的三點用圖可表示為:
藍色線代表網路讀取,紅色線代表執行時間,這兩個都是針對腳本的;綠線代表HTML 解析。
總結:
由於現代瀏覽器都存在prefetch,所以defer, async 可能並沒有太多的用途,可以作為了解擴展知識,僅僅將腳本文件放到body 底部就可以起到很不錯的優化效果。
defer 和 async 都是非同步載入腳本檔案。
慎用async,因為它完全不考慮依賴關係,只要下載完後就加載,不考慮此時頁面樣式先後的加載順序,不過它對那些可以不依賴任何腳本或不被任何腳本依賴的腳本來說卻是非常合適的,最典型的例子:Google Analytics。
耗時較長的腳本程式碼可以使用 defer 來延遲執行。
更多程式相關知識,請造訪:程式設計影片! !
以上是js和css檔案位置對頁面效能有什麼影響?的詳細內容。更多資訊請關注PHP中文網其他相關文章!