挺久以前就有網友給我的 GitHub Pages 博客模板提 Issue,說希望能增加 CDN 用於加速靜態資源的加載,由於懶,一直沒有動。
最近偶爾要打開自己博客看下 Wiki 的時候,要等挺久,比較痛苦,碰巧昨天晚上看到這樣一篇帖子:GitHub 圖床的正確用法,通過 jsDelivr CDN 全球加速,感覺很適合我的需求場景,於是決定趁這幾天休假將這個改造一下。
先看效果
以下改造前后的加載情況都是在 Edge 瀏覽器禁用緩存后錄制的,錄制時間段很接近,從本地訪問兩個 GitHub Pages 服務的原始響應速度應該類似。
改造前加載
注:由於改造前沒有保留加載圖,所以這是截的一個使用相同模板的朋友的首頁加載情況。
可以看到耗時最長的兩個請求時間達到了 12 秒左右,而且很多資源的加載時間在 1 秒以上,頁面完成加載時間長達 15 秒多……估計一般的訪客是沒這個耐心等待的。
改造后加載
這樣一對比效果還是很明顯的。改造過后耗時最長的是兩個沒辦法走 CDN 的請求,而走 CDN 的那些資源加載時間基本都沒超過 60 毫秒,頁面完成加載時間縮短到了 3 秒以內。
當然,因為頁面自身還是在 GitHub Pages 托管,有時候首個請求還是會挺久才返回。
改造后的效果可以打開 https://mazhuang.org 體驗。
方案考慮
優化獨立博客的加載速度有一些不同的思路,對應不同的方案:
- 優化博客代碼,精簡需要加載的資源;
- 將博客部署到國內訪問快的服務器上;
- 部署到國內的代碼托管平台,比如 Gitee 和 Coding 等;
- 采用 CDN 加速;
- 等等。
其中 2 和 3 我不想考慮,還是期望只在 GitHub 上管理博客,所以 1 和 4 是優化方向,本文對應的就是 4 的部分。
而采用 CDN 加速的方案,可以考慮
-
將公共庫改為直接引用公共 CDN 鏈接;
-
自己編寫和修改的靜態資源自己去托管在一個 CDN 服務上。
有一些 CDN 服務商提供一定的免費額度,可以按喜好選用,或者選擇付費服務。這里我沒有糾結,看完文首提到的那篇文章,去看了下 jsDelivr 的介紹后覺得靠譜:它原生支持使用 GitHub 項目里的資源,什么都不用配置,更重要的是免費,在國內有節點,而且速度還不錯(官網上也把 works in China 作為一個賣點的),遂決定直接用它。
jsDelivr 支持的 GitHub 資源的方式
jsDelivr 對 GitHub 的支持是作為重要特性來宣傳的,官網的介紹鏈接:https://www.jsdelivr.com/features#gh,以下是一些認為需要了解的知識的小結:
這里以我托管博客的 GitHub 倉庫為例,地址是 https://github.com/mzlogin/mzlogin.github.io
,那它里面的資源可以直接以 https://cdn.jsdelivr.net/gh/mzlogin/mzlogin.github.io/
+ 倉庫里的文件路徑
來訪問。
比如倉庫里有一個 js 文件 assets/js/main.js
,那么它可以用 CDN 鏈接 https://cdn.jsdelivr.net/gh/mzlogin/mzlogin.github.io/assets/js/main.js
來訪問。
另外還支持一些高級用法,比如:
-
指定 release 版本號/提交 sha1/分支名稱,例如指定獲取該倉庫的名稱為
1.2.0
或v1.2.0
的 release 版本資源:https://cdn.jsdelivr.net/gh/mzlogin/mzlogin.github.io@1.2.0/assets/js/main.js
如果指定版本為
1
或者1.2
,那它會自動匹配到這個范圍內的最新版本號。也可以不指定版本或者指定版本為
latest
,這樣總是使用最新版本的資源。 -
壓縮資源,在 js/css 文件后綴前面加上
.min
:https://cdn.jsdelivr.net/gh/mzlogin/mzlogin.github.io@1.2.0/assets/js/main.min.js
-
合並多個文件,用
combine/file1,file2,file3
格式的鏈接:https://cdn.jsdelivr.net/combine/gh/mzlogin/mzlogin.github.io@1.2.0/assets/js/main.min.js,gh/mzlogin/mzlogin.github.io@1.2.0/assets/js/simple-jekyll-search.min.js
壓縮資源、合並文件的 CDN 鏈接在第一次有人訪問時可能比較慢,后面再有人訪問就快了。
其它知識點:
- 可以通過
https://cdn.jsdelivr.net/combine/gh/mzlogin/mzlogin.github.io[@<版本號>]/[<文件夾>/]
這樣的路徑瀏覽緩存文件列表; - 可以訪問
https://purge.jsdelivr.net/gh/mzlogin/mzlogin.github.io@1.2.0/assets/js/main.js
來清除指定文件的緩存;(將引用的 CDN 鏈接里的cdn
改成purge
就是了) - 可以訪問
https://data.jsdelivr.com/v1/package/gh/mzlogin/mzlogin.github.io
來查看 CDN 上的 tags 和 versions 列表,更多數據接口參數參見 https://github.com/jsdelivr/data.jsdelivr.com。
改造步驟
下面是記錄具體改造博客模板的步驟:
-
在 _config.yml 文件中添加控制開關:
# 對 css 和 js 資源的 cdn 加速配置 cdn: jsdelivr: enabled: true
-
修改 _layouts 里的文件,給名為
assets_base_url
的變量賦值,用它來代表加載靜態資源的根路徑:{% raw %}
{% assign assets_base_url = site.url %} {% if site.cdn.jsdelivr.enabled %} {% assign assets_base_url = "https://cdn.jsdelivr.net/gh/" | append: site.repository %} {% endif %}
{% endraw %}
-
修改以前直接用 {% raw %}
{{ site.url }}
{% endraw %} 拼接的靜態資源引用鏈接,替換為 {% raw %}{{ assets_base_url }}
{% endraw %},比如 _includes/header.html 里:{% raw %}
- <link rel="stylesheet" href="{{ site.url }}/assets/css/posts/index.css"> + <link rel="stylesheet" href="{{ assets_base_url }}/assets/css/posts/index.css">
{% endraw %}
這樣萬一哪天 CDN 出了點什么狀況,我們也可以很方便地通過一個開關就切回自已的資源鏈接恢復服務。
主要就是這類修改,當然涉及的地方有多處,以上只是舉一處例子記錄示意,改造過程和改造后的代碼可以參考我的博客倉庫 https://github.com/mzlogin/mzlogin.github.io。
現存問題
-
如果項目曾經打過 tag,那么新增/修改靜態資源后,需要刷新 CDN 緩存的話,需要打個新 tag;
一般發生在修改了博客模板的 js/css 以后。我也還在摸索如何省去這一步的方法。
Update: 我后來采用的解決方法是刪除了所有的 tag,這樣以前的 release 就變成了 Draft,對外是不可見的,因為我這個倉庫不需要對外可見的 release,所以這個問題也就解決了,不需要再操心刷新 CDN 的問題了。