上一篇曾提到,咱们可对资源加密存储,而后在 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 的使用限制,也是须要咱们思考的。