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--
發表於 2013-12-25 22:18:14 ,並被新增「 CSS 、 Webkit 」標籤 。檢視本文 Markdown 版本 »
提醒:本文最後更新於 1792 天前,文中所描述的資訊可能已發生改變,請謹慎使用。