免費 CDN 玩法 —— 將整個網站打包成一個圖片文件


資源合並

前端開發者都知道,過多的請求對性能影響很大。而且有些 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 Cache 供 Service Worker 使用。

至於穩定性,可將圖片上傳到多個站點作冗余。如果加載失敗或 Hash 不正確則使用下一個備用圖片,從而大幅提升穩定性和安全性。

隱私保護

為了盡可能減少隱私泄露,同時防止外鏈限制,我們可通過 referrer-policy 對 referrer 進行隱藏。例如:

var img = new Image()
img.crossOrigin = true
img.referrerPolicy = 'no-referrer'
img.src = 'https://pic3.zhimg.com/80/v2-a492fc0204ad0275e0b609ceac2dab10.png'

這樣請求中就沒有 Referer 頭了。

不過需注意的是,為了能讀取圖片中的像素數據,必須使用 CORS 模式,即設置 crossOrigin 屬性。這種模式下請求會出現 Origin 頭。雖然大部分網站不會使用該頭限制外鏈,但它會泄露你的站點域名。這仍不完美。

為了能隱藏 Origin 請求頭,這里使用一種簡單古老但有效的黑科技 —— 使用無源的頁面加載圖片,例如通過 Data URI 創建的 iframe:

var iframe = document.createElement('iframe')
iframe.src = `data:text/html,
<script>
  var img = new Image();
  img.crossOrigin = true;
  img.src = 'https://pic3.zhimg.com/80/v2-a492fc0204ad0275e0b609ceac2dab10.png';
</script>
`
document.body.appendChild(iframe)

這個方案雖然無法讓 Origin 請求頭消失,但可將其設置為 null,從而保護你的站點域名不被泄露。

圖片加載完成后,再通過 postMessage 將像素數據發送給主頁面。這樣即可同時隱藏 RefererOrigin 信息,最大程度防止隱私泄露!

演示

基於上述思路,這里實現了一個簡單的工具,暫且稱之 web2img

GitHub: https://github.com/EtherDream/web2img

工具演示:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM