1. 程式人生 > >Webkit 非同步載入 CSS 的奇怪現象

Webkit 非同步載入 CSS 的奇怪現象

文章目錄

提醒:本文最後更新於 1792 天前,文中所描述的資訊可能已發生改變,請謹慎使用。

通常,css 檔案都會放在頁面 head 引入,這樣可以避免頁面載入時由於樣式缺失而引發的跳動。有時候,我們也有需要非同步載入 css 檔案的場景,如按需載入某些自帶外鏈 css 的模組。本文描述我在 webkit 下非同步載入 css 檔案遇到的兩個奇怪現象。

另外,本文提到的問題要想繞過去很容易,有各種各樣的辦法,本文只描述現象本身。

首先來看本文測試用到的 loadCss 函式:

var loadCss = function(i) {
    var
defererd = when.defer(); var link = document.createElement('link'); link.type = 'text/css'; link.rel = 'stylesheet'; var css = cssList[i]; link.onload = function() { log(css + ' is ok.'); defererd.resolve(); }; link.href = base + css; document.getElementsByTagName('head'
)[0].appendChild(link); return defererd.promise; };

這裡引入 promise 是為了後面併發載入 css 更方便。另外由於本文只考慮 webkit,而比較新的 webkit 支援了 link 的 onload 事件,所以這裡不需要做相容性處理。這部分程式碼很簡單,不需要多解釋。

奇怪現象一

試想,用上面的函式併發載入多個 css,瀏覽器會在什麼時候解析並應用它們呢?是每個 css 下載完單獨生效,還是要等到全部 css 都下載完再一起生效呢?我之前一直認為是前者,firefox 確實是前者,但 webkit 的表現卻是後者。

var
cssList = ['css1.php', 'css2.php', 'css3.php']; when.all([loadCss(0), loadCss(1), loadCss(2)]).then(function() { log('all done.'); log('time:' + (new Date - date)); });

以上程式碼,一共併發載入了三個 css,它們分別被我在服務端 delay 了 1s、3s 和 5s。測試結果表明,webkit 會等最慢的那個下載完才開始應用這三個 css 檔案。通過下圖可以清楚看到,前兩個 css 下載完,UI 並不會得到更新。

這裡是測試地址,請用最新的 safari 或 chrome 測試,並留意控制檯的日誌。

這個現象背後,應該是 webkit 對 css 渲染所做的優化:為了避免造成頁面元素抖動(如前後兩個 css 都有作用於某個元素的規則),在尚有 css 未載入完時,不進行渲染。個人覺得比較好的做法是設定一個超時,避免某些特別慢的 css 請求造成頁面傻等。不知道 webkit 是否有這樣的機制,但我嘗試把本例中某個 css 的 delay 改到 30s,結論還是一樣。總之這個策略有點奇怪。

奇怪現象二

我發現的另外一個奇怪的現象是,webkit 在非同步載入 css 時,UI 更新會變得很節制,一定要移動滑鼠或者滑鼠點選才更新。

這個現象測試地址在這裡,用最新的 safari 或者 chrome 都能復現。

本例中,我用前面寫的 loadCss 函式載入了一個 delay 30s 的 css,同時啟動定時器,每 200ms 更新一次頁面某元素的內容,但在 webkit 中,如果滑鼠不動,或者滑鼠移出了瀏覽器視窗,頁面就靜止不變了。

我們知道,現代瀏覽器都會在瀏覽器視窗不可見時提高定時器間隔,但上面這個現象明顯跟這沒關係。不知道這次又觸發了 webkit 什麼優化策略,總之更奇怪。

更新 @ 2016.04.12:本文描述的現象一在 Chrome 50+ 中已修復;現象二也已修復(具體修復版本沒有考證);Safari 中依舊還是老樣子。

--EOF--

提醒:本文最後更新於 1792 天前,文中所描述的資訊可能已發生改變,請謹慎使用。