上一篇曾提到,咱們可對資源加密存儲,而後在 SW 中進行解密。前端
理論上這固然可行,但事實上會出現一些問題:咱們必須等整個資源下載完成後,才能開始解密操做。這對於用戶體驗,會產生很大的影響。git
假若有個 1MB 的圖片,經過 100 KB/s 的速度加載,那麼要 10 秒後才能解密再展現;然而正常狀況下,圖片是邊加載邊顯示的,並不會讓用戶等好久,而後一次性展現全部的。github
爲了解決這個問題,一個期待已久的新標準終於到來,那就是 Stream API。瀏覽器
有了流的支持,數據就能夠漸進處理,而沒必要等待完整的。例如,咱們使用 fetch 分塊讀取內容:網絡
// fetch 分塊讀取演示 async function load(url) { let res = await fetch(url); console.log('response:', res); let reader = res.body.getReader(); for (;;) { let r = await reader.read(); if (r.done) { break; } console.log('chunk:', r.value); } console.log('end'); } load('https://raw.githubusercontent.com/EtherDream/_/master/pic.jpg');
演示:codepen.io/anon/pen/zPKrGX多線程
同時,SW 也支持數據分塊輸出給下游:負載均衡
// SW 分塊輸出 let stream = new ReadableStream({ start(controller) { ... input.ondata = function(chunk) { controller.enqueue(chunk); }; input.onend = function() { controller.close(); }; ... } }); let res = new Response(stream, ...); ...
二者結合,咱們就能夠實現邊下載、邊解密、邊輸出的效果。因而對於加密的圖片、視頻等資源,也能按部就班地展現了!async
下載加速除了解密、解壓縮等場合,數據流還可用於傳輸優化。例如,用戶下載大文件的場合。ide
因爲免費空間單個節點的帶寬是有限的,所以下載速度不會太快。這時就能夠經過 SW 作加速了 —— 咱們同時從多個節點獲取相應的文件片斷,而後依次輸出到響應流裏:fetch
在用戶看來,這只是瀏覽器默認的單線程下載,但事實上內部已經過 SW 加速,和傳統的多線程下載軟件並沒有本質區別!
固然,就算免費空間不支持 Range 請求也不要緊,咱們可事先把大文件分紅多個小文件上傳,而後分別加載便可。
動態加速上一篇提到,經過 SW 可對故障節點「實時無縫」的切換。如今有了數據流,咱們可將其發揮到極致,甚至能在傳輸的過程當中進行調整。
例如,SW 默認選擇節點 1 加載資源,但發現速度沒有預期的那麼快,因而可增長節點 2 參與加速:
這樣,咱們就能根據用戶的實際網絡狀況,在端上動態調整,從而實現更智能的負載均衡!
插入腳本有時候,咱們但願給站點下全部頁面的頭部插入一個 JS 腳本。
這個功能,若是沒有數據流支持的話,那麼 SW 必須得下載整個 HTML 才能修改;而如今,咱們只需改造最早返回的幾個 chunk 便可!
不過須要注意的是,chunk 是二進制層面截斷的,所以可能把多字節字符截成兩半,致使出現亂碼。
爲此,咱們須要用「流模式」解碼字符串。例如:
// stream decode example let dec = new TextDecoder(); let chunk1 = new Uint8Array([228, 189, 160, 229, 165]); let chunk2 = new Uint8Array([189]); dec.decode(chunk1, {stream: true}); // "你" dec.decode(chunk2, {stream: true}); // "好"
若是 chunk 末尾的字符不完整,那麼不足的部分則被暫存在內部,下次解碼時會自動加在開頭。
這樣,咱們就能用字符串方法,更方便地操做二進制數據了:
let dec = new TextDecoder(); let enc = new TextEncoder(); input.ondata = function(chunk) { // 二進制 -> 字符串 let str = dec.decode(chunk, {stream: true}); // 插入腳本元素 str = str.replace(/<head/i, '<script ...><head'); // 字符串 -> 二進制 chunk = enc.encode(str); ... };
固然,這裏的邏輯還有點瑕疵 —— 假如 <head 這個字符串正好跨越兩個 chunk,那就沒法匹配到了。
因爲 JS 不支持流模式的正則匹配,所以能夠用個土辦法:若是 str 匹配不到,則截掉末尾 5 個字符,而後將尾巴暫存起來,拼到下一次的頭部。。。這樣雖然沒有流那麼嚴格,但實現簡單,而且也很高效。
此外,因爲咱們只需替換一次,所以以後可跳過這步,無需解碼、匹配、編碼了。
小結在數據流的配合下,SW 可實現很是豐富的玩法。不過目前只有 Chrome 瀏覽器支持 Stream API,所以兼容性也是個較大的問題。相信隨着新標準的普及,從此使用前端加速的網站,必定會愈來愈多。
然而對於咱們的「免費空間」來講,除了兼容性問題以外,還有 SW 的各類使用限制也是一個挑戰。所以如何繞過 SW 的使用限制,也是須要咱們思考的。