js禁止页面后退 W3CTPAC会议总结:复苏(Resurrection)最终被杀死
上个月,我们在福冈举行的 W3C TPAC 会议上召开了 service worker 会议。这是几年来我们第一次专注于潜在的新功能和行为。现总结如下:
复苏(Resurrection)最终被杀死
<pre style="font-size: inherit;color: inherit;line-height: inherit;">1reg.unregister();
</pre>
如果你取消 service worker 注册,则会将其从注册列表中删除,但它仍会继续控制现有页面。这意味着它不会中断任何正在进行的提取等操作。不过一旦所有这些页面都消失了,就会被垃圾回收。
但是在规范中有一个地方讲到:如果一个名为 serviceWorker.register() 的页面具有相同的作用域,则被注销的 service worker 注册将会“复苏”。我不知道为什么要这么做。无论如何,这都是一个愚蠢的主意,因此我们把它删除了()。
<pre style="font-size: inherit;color: inherit;line-height: inherit;">1// Old behaviour: 2const reg1 = await navigator.serviceWorker.getRegistration(); 3await reg1.unregister(); 4const reg2 = await navigator.serviceWorker.register('/sw.js', { 5 scope: reg1.scope, 6}); 7console.log(reg1 === reg2); // true!
</pre>
好吧,如果 reg1 不控制任何页面,那可能是错误的。是的,这令人困惑。
<pre style="font-size: inherit;color: inherit;line-height: inherit;">1// New behaviour: 2const reg1 = await navigator.serviceWorker.getRegistration(); 3await reg1.unregister(); 4const reg2 = await navigator.serviceWorker.register('/sw.js', { 5 scope: reg1.scope, 6}); 7console.log(reg1 === reg2); // Always false
</pre>
现在,保证 reg2 是一个新的注册。复苏已被杀死。
我们在 2018 年就此达成了共识,并已在 Chrome 中实现,同时在 Firefox 和 Safari 中也已经实现。
self.serviceWorker
在 service worker 中,很难获得对自己的 ServiceWorker 实例的引用。用 self.registration 可以访问你的注册,但是究竟哪个 service worker 代表你当前正在执行的服务呢?self.registration.active?也许是吧,也或许是 self.registration.waiting 或 self.registration.installing,或者都不是。
作为代替:
<pre style="font-size: inherit;color: inherit;line-height: inherit;">1console.log(self.serviceWorker);
</pre>
上面的内容将为你提供引用,无论其处于什么状态。
这项小功能已在所有浏览器中达成共识,在 Chrome 中正在积极开发。
页面生命周期和 service workers
我是 page lifecycle API () 的忠实拥护者,因为它标准化了多年来浏览器已经完成的各种行为,特别是在手机上,例如,撤下页面以节省内存和电池。
此外,会话历史记录 (#the-session-history-of-browsing-contexts)可以包含 DOM 文档,这通常称为“后-转发页面缓存”或“ bfcache”。大多数浏览器中已经存在了许多年,这是 Chrome 的最新版本()。
这意味着页面可以是:
我们需要弄清楚这些状态怎样适合特定的 service workers 行为:
我们决定:
我甚至对所有的情况进行了测试:
现在我们只需要指定它。
将状态附加到客户端
当我们讨论页面生命周期的内容时,Facebook 的同事提到了他们如何用 postMessage 向客户询问其状态,例如“用户当前是否在键入消息?”。我们还注意到,我们已经讨论过向客户端添加更多状态(大小、密度、独立模式、全屏等),但是很难划清界线。
相反,我们讨论了允许开发人员将可克隆的数据附加到客户端,这些数据将显示在 service worker 的客户端对象上。
<pre style="font-size: inherit;color: inherit;line-height: inherit;">1// From a page (or other client): 2await clients.setClientData({ foo: 'bar' });
</pre>
<pre style="font-size: inherit;color: inherit;line-height: inherit;">1// In a service worker: 2const allClients = await clients.matchAll(); 3console.log(allClients[0].data); // { foo: 'bar' } or undefined.
</pre>
现在还处于早期,但感觉是这样可以避免在 postMessage 上来回移动。
立即注销 worker
如前所述,如果你注销 service workers 注册,则会从注册列表中将其删除,但是它将慧继续控制现有页面。这意味着它不会中断正在进行的提取等操作。但是在某些情况下,无论中断什么事情,你都希望 service workers 立即离开。
这里的一个客户端是 Clear-Site-Data ()。如上所述,它正在注销 service workers,但是 Clear-Site-Data 是“立即摆脱一切”开关,因此当前行为不太正确。
常规注销将保持不变,但是我将指定一种方法来立即注销 service worker,这可能会终止正在运行的脚本并中止正在进行的提取。Clear-Site-Data 将使用此方法,但我们也可以将其公开为 API:
<pre style="font-size: inherit;color: inherit;line-height: inherit;">1reg.unregister({ immediate: true });
</pre>
来自 LinkedIn 的 Asa Kusuma 已编写了 Clear-Site-Data 测试 ()。我只需要进行规范工作就可以了,不幸的是说起来容易做起来难。
URL 模式匹配
这是一个很大的问题。我们在整个平台上都使用 URL 匹配,尤其是在 service worker 和 Content-Security-Policy () 中。但是匹配非常简单——完全匹配或前缀匹配。开发人员倾向于使用 path-to-regexp 之类的东西。Ben Kelly 提议我们将类似的东西带到平台上。
它需要比正则表达式路径更具限制性,因为我们希望能够在共享进程(例如:浏览器的网络进程)中处理这些问题。RegExp 确实很复杂,并且可以进行各种拒绝服务攻击。浏览器供应商对开发人员有意锁定自己的网站感到满意,但我们并不想锁定整个浏览器。
这里是 Ben 的提案 ()。这非常雄心勃勃,但是如果我们可以跨平台使用更富有表现力的 URL 那就太好了。这样的例子非常引人注目:
<pre style="font-size: inherit;color: inherit;line-height: inherit;">1// Service worker controls
/foo and
/foo/*, 2// but does not control
/foobar. 3navigator.serviceWorker.register(scriptURL, { 4 scope: new URLPattern({ 5 baseUrl: self.location, 6 path: '/foo/?*', 7 }), 8});
</pre>
把流作为请求体
多年来,你可以流式传输响应:
<pre style="font-size: inherit;color: inherit;line-height: inherit;">1const response = await fetch('/whatever'); 2const reader = response.body.getReader(); 3 4while (true) { 5 const { done, value } = await reader.read(); 6 if (done) break; 7 console.log(value); // Uint8Array of bytes 8}
</pre>
规范还说,你可以将流用作请求的主体,但没有浏览器实现。但是,Chrome 已决定再次使用它,而 Firefox 和 Safari 表示也会这样做。
<pre style="font-size: inherit;color: inherit;line-height: inherit;"> 1let intervalId; 2const stream = new ReadableStream({ 3 start(controller) { 4 intervalId = setInterval(() => { 5 controller.enqueue('Hello!'); 6 }, 1000); 7 }, 8 cancel() { 9 clearInterval(intervalId); 10 }, 11}).pipeThrough(new TextEncoderStream()); 12 13fetch('/whatever', { 14 method: 'POST', 15 body: stream, 16 headers: { 'Content-Type': 'text/plain; charset=UTF-8' }, 17});
</pre>
上面的代码将“hello”作为单个 HTTP 请求的一部分,每秒钟一次发送到服务器。这个例子是愚蠢的,但是它展示了一项新功能——在你获得整个请求体内容之前将数据发送到服务器。当前,你只能分块或使用 websocket 来执行此操作。
一个实际的例子是涉及上传流式传输的内容。例如你可以在编码或录制的时候上传视频。
HTTP 是双向的。该模型不是先请求后响应——你可以在仍然发送请求正文的同时开始接收响应。但是,在 TPAC 大会中,浏览器开发人员注意到,鉴于当前的网络栈,在获取过程中公开这个内容确实很复杂,因此请求流的最初实现在请求完成之前不会产生响应。这还算不错——如果你想模拟双向通信,则可以使用一次 fetch 进行上传,而用另一次进行下载。
响应后执行
这已成为 service workers 中非常普遍的模式:
<pre style="font-size: inherit;color: inherit;line-height: inherit;"> 1addEventListener('fetch', event => { 2 event.respondWith(async function() { 3 const response = await getResponseSomehow(); 4 5 event.waitUntil(async function() { 6 await doSomeBookkeepingOrCaching(); 7 }); 8 9 return response; 10 }()); 11});
</pre>
但是,有些人发现他们在 waitUntil 中运行的某些 JavaScript 延迟了 return responsejs禁止页面后退,并用了 setTimeout hack 来解决。
为了避免这种 hack 操作,我们同意使用 event.handled,这是一个 promise,一旦 fetch 事件提供了响应或将其推迟给浏览器后便会解决。
<pre style="font-size: inherit;color: inherit;line-height: inherit;"> 1addEventListener('fetch', event => { 2 event.respondWith(async function() { 3 const response = await getResponseSomehow(); 4 5 event.waitUntil(async function() { 6 // And here's the new bit: 7 await event.handled; 8 await doSomeBookkeepingOrCaching(); 9 }); 10 11 return response; 12 }()); 13});
</pre>
后台同步(background sync)和后台获取(background fetch)的隐私问题
Firefox 有后台同步 ()的实现,但由于隐私问题而被阻止,这是由 Apple 员工共享的。
当用户处于“在线”状态时,后台同步会为你提供 service worker 事件,该事件可能会立即消失,也可能会在用户离开站点后的某个时间出现。由于用户已经作为顶级页面访问了该网站(例如原始位置在URL栏中,而不是 iframe),因此 Chrome 很高兴在以后允许一个小的,保守的执行窗口。Facebook 已经尝试过这种方法来发送分析数据并确保聊天消息的传递,而且发现了它的性能比 sendBeacon 之类的方法更好。
Mozilla 和 Apple 员工对后台获取()模型更加满意,该模型在获取期间会持续显示通知,并允许用户取消。
Google搜索已使用后台同步来在线获取内容,但是他们可以用后台获取来达到类似的目的。
这次讨论并没有真正得出结论,但我感觉苹果公司可能实现了后台获取而不是后台同步。Mozilla 也可能会做同样的事情,或者使后台同步变得更加用户可见。
内容索引
Rayan Kanso 提出了内容索引提案 (),它允许网站可以声明能够脱机使用的内容,因此浏览器或 OS 可以在其他位置(例如 Chrome 中的新标签页)显示此信息。
有人担心,无论这些东西出现在什么UI上,网站都可以使用它来发送垃圾邮件。但是,浏览器可以自由地忽略或验证所告知的任何内容。
这是个非常新的提案,它已作提交给小组。
启动事件
Raymes Khoury 向我们提供了有关启动事件提案 ()的最新信息。这是 PWA 控制多个窗口的一种方式。例如js禁止页面后退,当用户单击指向你网站的链接,但是没有明确建议网站应如何打开(例如“在新窗口中打开”)时,如果开发人员可以决定是将焦点集中在网站使用的现有窗口上还是打开新窗口,那将是很好的选择。这反映了当今原生应用的工作方式。
同样,这项工作正在进行中。
发表评论
热门文章
Spimes主题专为博客、自媒体、资讯类的网站设计....
一款个人简历主题,可以简单搭建一下,具体也比较简单....
仿制主题,Typecho博客主题,昼夜双版设计,可....
用于作品展示、资源下载,行业垂直性网站、个人博客,....
Z.
4天前
博主你好,Deng插件,这个点击不进去,提示这个(Warning: require_once(/www/wwwroot/w.zzy2020.com/usr/plugins/Deng/Deng/html/profile.php): failed to open stream: No such file or directory in /www/wwwroot/w.zzy2020.com/Fresh/extending.php on line 26
Fatal error: require_once(): Failed opening required '/www/wwwroot/w.zzy2020.com/usr/plugins/Deng/Deng/html/profile.php' (include_path='.:/www/server/php/72/lib/php') in /www/wwwroot/w.zzy2020.com/Fresh/extending.php on line 26)