各位看官,96分!這種事情當然要拿出來炫耀的呀!
其實會被 PageSpeed 挑出來的問題,都不算太難解決,最麻煩的就是這兩個了:
- 優先處理要顯示的內容
- 清除前幾行內容中的禁止轉譯 JavaScript 和 CSS
PageSpeed 的標準不難理解,要以最快的速度,將網頁內容顯示在使用者的可視範圍內,可視範圍就是瀏覽器不用捲動的那個區域,所以:
- CSS 只要足夠讓網頁正確顯示在可視範圍就夠了
- 為了讓內容盡快出現,所有的外部 CSS 和 JavaScript 檔案,都要用非同步的方式載入,或者等待網頁內容出現後再載入
提醒你,如果 web server 是自己的,一定要先從 web server 開始先改善 (例如 expires、reverse proxy……),再來才是改善網頁。
首先
先把 Google 給的說明、建議、資料等,都先看一看。
優先處理要顯示的內容
說起來很簡單,只要把現有的 CSS 中,和可視範圍中有關的挑出來就可以了,但是如果有用Bootstrap或W3.CSS這類東西的,不就哭哭了!
其實你只要到https://www.sitelocity.com/critical-path-css-generator,把你的網址給他,大約數十秒,他就幫你挑好,電腦不只會挑花生,也會挑 CSS 喔!
記得喔!你的 CSS 必須是在 HTML 裡面載入的,這樣網站才看得懂。
網站有詳細的說明,從「為什麼」開始詳細說明,就照他的說明,我是建議直接塞到 head 裡面,免得你的問題變成俄羅斯娃娃。
當然,sitelocity 挑出來的,是基於目前的網站內容,以後網站內容有變動,這裡當然也要修改;如果只是改改顏色之類,不會變動到排版 (包括大小),就沒有關係。
清除前幾行內容中的禁止轉譯 JavaScript 和 CSS
請先閱讀關鍵轉譯路徑這篇文章,主要是在講 CSS 和 JavaScript 對於瀏覽器顯示網頁內容效率的影響。
轉譯就是 rendering,比較常見到的翻譯是「渲染」,常常看到跟 GPU 相關的文章一起出現;這兒講的是,瀏覽器把 HTML、CSS、font…等一堆東西搓成一團,終於顯示出網頁的這段過程。
非同步載入 JavaScript 外部檔案
這個問題應該不大,如果你還有這個問題的話,建議你直接導入RequireJS,雖然 RequireJS 功能非常多,不過你只要使用非同步載入 JavaScript 的功能就好。
非同步載入 JavaScript 當然一點也不稀奇,不過非同步載入的檔案一多,載入完成的先後順序,和彼此的相依性就很麻煩,這個時候,就用 RequireJS 處裡就好,雖然原先的 code 得作些修改。
但是即使你不用 RequireJS,還是得修改 code 去解決先後順序、相依性的問題,不如一鼓作氣,就用 RequireJS 吧!
非同步載入 CSS 外部檔案
一般的載入方式就是同步的:
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css" media="all">,這個會阻擋瀏覽器其他工作,因為要等檔案載入、解析……。
反正就是要用非同步的方式載入CSS 檔案,以下是我的方式:
先 <head> 內,所有載入外部 CSS 檔案的 <link>,搬到 <body> 的底下將 <link ...> 改成 <async-css data-href='xxx.css'></async-css>,除了改 tag 以外,原先的 href 改成 data-href,其他都拿掉延伸閱讀:MDN 的 Custom Element- 將原先用 <link ...> 載入的 CSS 檔案,改用 JavaScript 先推到一個 [] 去
- 接下來, 那些 CSS 外部檔案,用 JavaScript,在 window 的 load 事件後再載入
Google 一下「async css」,就可以找到很多說明、作法,以下是我的作法;首先,在 HTML 上頭:
_blogger.loadCss = function(){_blogger.loadCss.queue.push(arguments);};
_blogger.loadCss.queue = [];
_blogger 是我用來當全域變數的,接下來,將要在 load 事件載入的 css,改成以下這個樣子:
<script>_blogger.loadCss('https://www.w3schools.com/w3css/4/w3.css','https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css');</script>
最後,在 load 事件載入 CSS 就好了。
(function() {
var loadCssFunc = function() {
var ss, href, i;
for (i = 0; i < arguments.length; i++) {
href = arguments[i];
if (_blogger.hostname && !href.match(/^(data:|http|\/\/)/i)) {
href = _blogger.hostname + href;
}
console.time(href+"_load");
ss = window.document.createElement('link');
ss.rel = 'stylesheet';
ss.media = 'none';
ss.href = href;
ss.id = 'async_'+(Date.now()+Math.floor(Math.random()*1e13)).toString(32);
ss.onload = function() {
this.media = 'all';
console.timeEnd(this.href+"_load");
this.onload = null;
};
document.getElementsByTagName('body')[0].appendChild(ss);
}
};
var loadCssOnLoad = function(){
document.getElementsByTagName('body')[0].dataset.dataLoaded = '1';
var i, j;
for (i = 0; i < _blogger.loadCss.queue.length; i++) {
for(j = 0; j < _blogger.loadCss.queue[i].length; j++) {
loadCssFunc(_blogger.loadCss.queue[i][j]);
}
_blogger.loadCss.queue[i] = null;
}
_blogger.loadCss = loadCssFunc;
delete _blogger.loadCss.queue;
};
if (document.getElementsByTagName('body')[0].dataset.dataLoaded === '1') {
loadCssOnLoad.apply();
} else {
window.addEventListener('load',loadCssOnLoad,{once:true});
}
}());
可以的話就直接放在 HTML 裡面,別再讓瀏覽器花功夫載入;程式很簡單,就是產生一個新的<link ... />,放在<body>的最後面,重點是 media='none';因為這是個沒用的 CSS 外部檔案,所以瀏覽器就不會停下來等,也就不會造成「阻擋」,接著用一個 onload 的事件,當檔案載入後,圖窮匕見,再把 media 改成 all (或 screen 也可以),這時瀏覽器才會發現被騙了。
目前是沒有收到過瀏覽器的抱怨。
window.onload 事件之後,一樣可以用 _blogger.loadCss() 載入 CSS 檔案;之後用 AJAX 更新的內容,要注意裡面有沒有要載入新的 CSS。
原本打算用 Custom Elements,除了看起來比較炫以外,CSS 本來就用 HTML tag 載入的,那麼一樣用 HTML tag,感覺上就很合理;不過 IE 的問題太難解決,有興趣的話可以看asyncLoad_customElements.js這個檔案。
大 CSS 檔案 vs. 小 CSS 檔案
假設載入了 10 個 CSS 檔案,就可能造成瀏覽器重新排版網頁 10 次 (延伸閱讀),這時候, PageSpeed 會說個往返路徑之類的原因,然後就扣分。
PageSpeed 的要求是「畢其功於一役」,在使用者看到網頁前,往返抓 CSS 檔案的次數要越少越好。
這年頭應該都用 less 或 scss 來對付 css 了,所以即使你愛用小檔案,還是可以保留一大堆 .less,然後用 less 的
Hosting JavaScript 和 CSS 檔案
因為 Blogger 並不讓人放 JavaScript 或者 CSS 檔案,所以我的是放在UpDog和Forge;這兒有一篇文章Static Web Hosting: Who’s Best?值得一讀。
UpDog
UpDog 非常簡單,直接用 Dropbox 或 Google 帳號登入、連上自己的 Dropbox 帳號,就好了,UpDog 的網站也是非常簡潔,功能大概就是登入和登出。
這個最大的好處,就是你的 site root 直接連到你的 Dropbox 指定目錄,所以存檔後,等檔案自己同步到 Dropbox,就 ok 了;此外,沒有頻寬限制 (因為苦主是 Dropbox)、有 https、一個 site 是免費的、有個免費網址https://ken73chen.updog.co、免費 site 會被塞一個小小的 updog.co 的 logo、只能放靜態檔案。
速度不快比較麻煩,traceroute 看是還要跑回美國。
Forge
為了達到虛榮的96分,所以搭著Forge一起用。
使用 Forge,當然是為了亞馬遜的 CDN,而且當然在台灣有節點,否則大家連都很快,只有自己連的慢,這樣不是很淒涼嗎!
Forge 的免費帳號,能有一個 site、一個月 1GB 頻寬、有版本控制、可以和 Dropbox (或 GitHub) 同步,又是 Dropbox,而且可以指定和 UpDog 同一個目錄,真是太感人了。
不像 UpDog,只要將檔案更新到 Dropbox 就完工,Forge 還要 deployment 的動作,如果 enable Auto-deployment,根據說明,檔案更新到 Dropbox 後,三十秒就自動 deployment 到 Forge。
我建議不要使用 Auto-deployment,先在 UpDog 好好測試,一個階段再 deploy 到 Forge,這樣就可以好好使用 Forge 的版本控制功能,而且還能知道每次更新或新增的檔案。
此外,每次 deployment 後,不同的版本會有不同的 CDN 路徑,Forge 會幫你修改 HTML 的內容;不過會改變的其實也只有一個數字,Forge 會放到 HTML 內 <meta name='forge-tag' value='forge-token:XXXXX'/>。
Forge 的管理界面,比 UpDog 複雜一千倍,不過還是不困難,摸一摸就熟悉了,有提供個 Web Hook 網址, 如果你要 deploy,又懶的登入 Forge,只要連網址,就會自動
deployment;如果你又懶的登入檢查是否 deployment 完工,就可以設定個 Outgoing Hook,這樣 Forge 就可以主動通知你 deployment 的結果。
最後
我是有特別的原因,才在 Blogger 上面作這些事情,誠心建議您,不需要在 Blogger上花這些時間!