資源合併
前端開發者都知道,過多的請求對效能影響很大。而且有些 CDN 不僅按流量收費,請求數也收費,如果網頁裡有大量小檔案,顯然不划算。
為此不少開發者將零碎的小檔案進行合併優化,例如 JS/CSS 合併在一起,圖片合併成精靈圖等。
不過傳統的合併方式有一定的侷限性,只能合併同類型的檔案。例如 JS、CSS 等文字格式的資料可以合併,但 JS 和圖片顯然無法合併,畢竟一個是文字格式,一個是二進位制格式。而且合併過程需對現有資源進行修改,最終釋出的檔案與原始檔案差異很大。
有沒有什麼辦法,可將任何型別的資源併成在一起,並且不改變原始檔案?
類似方案
Google 推出了一個 Web Bundles
方案,可將任意型別的資源打包成一個檔案:
細節可參考:https://web.dev/web-bundles/
不過 Web Bundles
注重的是離線分享。如文中所提到,在沒有網路的飛機上,可將網頁小遊戲通過單個檔案的方式分享給旁邊的人一起玩。
演示可見,通過本地檔案開啟的網站仍保留原始 URL。
由於 Web Bundles
目前仍未正式啟用,需在 flags 中手動開啟,因此該方案仍無法解決本文提出的問題。
通用方案
事實上,我們大可不必關心資源型別,將所有檔案都當做二進位制檔案合併在一起,執行時再通過 JS 提取。
但是,網頁裡的資源仍然引用的是原始 URL,例如 <img src="a.gif">
。怎樣才能讓網頁使用 JS 提供的資料,而不是從原始 URL 載入?
這就需要藉助 HTML5 的一個黑科技 —— Service Worker。它能攔截網頁產生的 HTTP 請求,並能控制返回內容。這樣即可實現所有資源都從單個檔案中提取!
初始化
既然要呼叫 Service Worker,那麼是否得修改現有的 HTML 檔案,在其中新增指令碼?
事實上不需要!使用者首次訪問時,無論訪問哪個路徑,後端都返回 Service Worker 安裝頁;安裝完成後頁面自動重新整理,這時請求即可被 Service Worker 攔截,從而使用資源包中的 HTML 檔案。
至於「訪問任何路徑都返回安裝頁」的效果其實很簡單,用 404.html
即可實現。
免費空間
雖然我們將資源請求數降低到只有 1 個,但流量仍然是存在的。並且任何一個資源更新都得重新下載整個資源包,導致流量成本進一步增長。
有沒有什麼辦法,可大幅降低流量成本?很簡單,使用免費 CDN 即可。你可將資源包釋出 GitHub、NPM 等空間,然後通過 jsdelivr、unpkg 等免費 CDN 加速。
這樣,你的網站只需提供 404.html
和 sw.js 兩個極小的檔案即可,其他所有內容都可從免費空間獲取!
演示站點:https://fanhtml5.github.io/
原始檔案:https://github.com/fanhtml5/test-site (多個檔案,總共數 MB)
釋出檔案:https://github.com/fanhtml5/fanhtml5.github.io (只有兩個,壓縮後不到 2kB)
圖片空間
類似 jsdelivr、unpkg 這麼好用的免費 CDN 並不多,用在這裡太過浪費,作為開發者也不建議過度使用它們。
我們可使用更低廉更廣泛的免費空間 —— 各大網站的貼圖相簿,例如知乎、B 站、簡書等文章的貼圖,它們不僅支援 CORS,而且允許空 referrer,完全可用於儲存資料。
參照之前寫的《利用 canvas 實現資料壓縮》文章,我們可將原始資料編碼成圖片畫素,從而可將任何檔案寫入圖片並上傳到相簿;執行時再解碼還原,將原始檔案寫入 Storage 快取供 Service Worker 使用。
至於穩定性,可將圖片上傳到多個站點。如果 Hash 不正確則使用下一個,從而大幅提升穩定性和安全性。
演示
基於上述思路,這裡實現了一個簡單的工具,暫且稱之 web2img
。
GitHub: https://github.com/EtherDream/web2img
工具演示: