PWA系列 -- Service Workers 概述
2018-11-24
# 背景信息
浏览器有很多种 workers,我们先看看 Web Workers。最常见的 Web Workers 有下面两种,
- Dedicated Worker,专用 Worker,只能被创建它的JS访问。
- Shared Worker,共享 Worker,可以被同一域名下的JS访问。 JavaScript 是单线程的,即一个浏览器进程中只有一个 JavaScript 的执行线程,同一时刻内只会有一段代码在执行。Web Worker 的目的,就是为 JavaScript 创造多线程环境,允许主线程将一些任务分配给子线程。Web Workers 一般是用于在后台执行一些耗时较长的 JavaScript,避免影响 UI 线程的响应速度。 Dedicated Worker 或 Shared Worker 最主要的能力是,
- 后台运行JS,不影响 UI 线程。
- 使用消息机制实现并行,可以监听 onmessage 事件。 我们可以看到 Dedicated Worker 和 Shared Worker 专注于解决“耗时的 JS 执行影响 UI 响应”的问题。而 ServiceWorker 则是为解决"Web App 的用户体验不如 Native App”的普遍问题而提供的一系列技术集合,显然 ServiceWorker 给它自己的使命更加远大。虽然规范把它定义为 Web Workers,但它已不是一个普通的 Worker 了。
# ServiceWorker 的能力
规范文档提到 ServiceWorker 是一个注册在指定源和路径下的事件驱动 Worker。官方入门文档提到它能提供丰富的离线体验,周期的后台同步,消息推送通知,拦截和处理网络请求,和管理资源缓存。这些技术细节我们在后续的文章逐一进行分析,现在我们先看看这些能力的意义。
- 丰富的离线体验 很多文档都会提到,ServiceWorker 能提供丰富的离线体验,但大家可能会想,我们好像很少需要离线的业务,甚至一些高政策风险的业务,连主文档都不允许缓存,那离线是否就没有实际意义了呢? 有实际意义的离线,一般不是指断开网络能访问,而是指在用户想访问之前,能提前把资源加载回来。离线并不是一直都断开网络,而是在网络连接良好的情况下,能把需要的资源都加载回来。一些比较糟糕的做法是在 WiFi 网络下把整个 App 客户端的资源都拉下来,这样其实很多资源是用户不需要的,浪费了用户的网络和存储。ServiceWorker 提供了更好更丰富的离线技术,Push + Fetch + Cache 这些技术结合能够提供非常完美的离线体验。比如,在某应用的小程序页面发版时,推送消息给应用客户端,客户端唤起页面的 ServiceWorker,去将需要用到的资源提前 Fetch 回来。
- 消息推送通知 ServiceWorker 的消息推送,其实是提供了一种服务器与页面交互的技术。消息推送,在 Native 或 Hybird App 已经比较常见。很多 Hybird App 里面其实还会有一些 Web 页面,在没有 ServiceWorker 消息推送之前,消息是推送不到页面的。消息能推送到页面,意味着页面提前知道要发生的一些事情,能提前把这些事情做好。
- 管理资源缓存 Web标准中提供了很多存储相关的 H5 API,比如,Application Cache,Local Storage等。但这些 API 都不是非常好用,主要原因在于给予页端的控制权太少,页端还是不能完全控制每一个资源请求的存储,或多或少会有一些趟不过的坑。ServiceWorker Cache API 的出现彻底改变了这一局面,让页面可以方便的操作每一个资源的存储。
- 拦截资源请求 一般来说,基于 Webview 的客户端拦截网络请求,都会基于 WebViewClient 的标准的 shouldInterceptRequest 接口。那么 ServiceWorker 的请求在 Webview 还能不能拦截呢?WebViewClient 的标准的 shouldInterceptRequest 接口无法拦截 ServiceWorker 的请求,但 Chrome 49.0 提供了新的 ServiceWorkerController 可以拦截所有 ServiceWorker 的请求。另外,页端可以监听 Fetch 事件,通过 FetchEvent.respondWith 返回符合期望的 Response,即页端也能拦截 Response。
- 发起资源请求 在 ServiceWorker 之前,页面一般通过 XMLHttpRequest 发起资源请求,但 XHR 有一定的局限性,比如,它不像普通请求那样支持 Request 和 Response 对象,也不支持 Streaming Response,一些跨域的场景也限制较多。而这些恰好是 ServiceWorker 的能力所在,Fetch API 支持 Request 和 Response 对象,也支持 Streaming Response,Foreign Fetch 还具备跨域的能力。 看了上面的能力,是不是觉得和 Native Apps 的能力很像?不错,ServiceWorker 对标的就是Native App,它的能力还在不断的扩展中,比如,Navigation Preload。
# ServiceWorker 的意义
Progressive Web Apps,一种能给 Web 带来令人惊叹的用户体验的新方式。里面提到三种最核心的用户体验。
- Reliable:即使在不确定的场景下(比如,网络不稳定,用户停留时间不确定),依然能提供可靠的Web服务。
- Fast:提供极速流畅的用户体验。
- Engaging:提供与 Native App 一致的用户体验。 我们看看 ServiceWorker 是否可以很好的达到上述要求?
- Background Sync 技术在页面已被关闭,或用户已退出应用,甚至用户已重启手机,它都能保证同步请求能正确地被触发,这就是提供可靠服务的一个例子。
- Fetch 技术和 Cache 技术能让页面完全控制资源请求和响应,提供了 HTTP Cache 层面操控缓存的能力,只要合理使用几乎能保证在页面加载之前,能把需要的资源都提前加载回来,把联网体验转换为离线体验,能极大的提升页面性能。
- Web Push 技术让页面服务器与页面交互成为可能,与 Fetch 技术结合可以提前让页面更新资源,与 Web Notifications 结合可以在需要的时候及时通知到用户。 我们相信,在不远的将来,很可能会有更加强大的技术加入到 ServiceWorker,例如让页面操控渲染流程的技术。
# ServiceWorker 的门槛
ServiceWorker 在国内的使用,有一定的门槛,
- 要求 HTTPS 页面 一般来说,能力越大责任越大,ServiceWorker 也不例外,它在提供了强大的能力的同时,也增加了安全风险。为了防止页面被劫持,ServiceWorker 是要求要在 HTTPS 页面下使用的。虽然 HTTPS 已是大势所趋,但对一些安全性要求不高的站点来说,成本还是挺大。
- Push 功能缺失 Push 的推送服务器,Chromium 默认使用 GCM/FCM,在国内都不能访问,无法使用。浏览器厂商自己搭建 Push 服务器,成本也不低,目前国内还未有浏览器厂商支持标准的 Push 服务。
- Background Sync 功能缺失 Chromium 默认的 Background Sync 功能,需要依赖 Google Play,同样在国内无法使用。
- iOS 仅支持基本功能 iOS 的 ServiceWorker 能力尚未达到 Android 的能力,今后是否能达到同等能力也是未知数。
# 总结
虽然 ServiceWorker 有一定的门槛,但其强大的能力可以为业务带来的各种体验上的优化,这点已经在阿里内部多个业务上得到了验证。我们期待开发者能在自己的业务中引入这项技术,如果您发现使用中出现了问题,使用中发现了需要内核优化的场景,使用中发现了新的需求,都可以联系我们。U4 内核技术团队愿意在提升 Web 能力上投入自己的技术力量并做出贡献。