作者 | 彭星
编辑 | 尾尾
一、回顾历史:移动时代之初,Web遭遇两大枷锁
当 Web 自信满满,步入移动时代之时,它还没有做好充足的准备。
回顾 2014 到 2015 年那段时间,作为前端开发人员,我正忙着前后端分离和体验优化。那时,我投入精力最多的就是移动站点的体验优化,例如提升首屏速度,提升动画流畅性等。为了达到更好的优化效果,我心力憔悴,HTTP 缓存、HTTP2 等一些优化技术都用上了,但体验依然比 Native App 差很多,始终无法突破移动设备上浏览器(和 WebView) 给 Web 带来的 用户体验枷锁。
除开 Web App 的体验问题,还有一个很重要的问题制约着 Web 在移动时代的发展,那就是第二个枷锁——用户留存枷锁。Native App 在安装完成之后会在桌面上有一个入口,后续用户触达 App 只有一步,而 Web App 在移动时代最主要的入口还是搜索引擎,用户从浏览器到站点需要经过搜索引擎,还需要记住和输入上次搜索的内容。当然,用户还可以直接输入站点 URL 地址,但这对于移动用户来说,无疑成本巨大,这就导致用户和 Web 站点之间的粘性非常脆弱。
两大枷锁禁锢了 Web 在移动端的发展,虽然移动 Web 可以触达的用户是 App 的三倍之多,但用户的留存时间却明显少于在 App 上的留存时间,参考 What is Progressive web App (and Why Should You Care):
https://codeburst.io/what-is-progressive-web-app-and-why-should-you-care-e397e24b1257
对比下来,互联网公司更愿意投入人力在 Native App 的开发上,而忽略 Web。因此,Native App 顺势迅速成长起来,大量的需求出现了,也吸引了各类培训机构开启相关 NA 开发的课程,吸引了很多人转做 NA 开发,各种 Native App 的技术、教程、实践等,也如雨后春笋般布满了各大技术论坛。
对于前期的失利,Web 显然是不甘心的。想要继续前进,就必须打造解开枷锁的钥匙——Progressive Web App( PWA ) 以及支撑 PWA 的一系列关键技术应运而生。
早在 2014 年, W3C 公布就公布过 Service Worker 的相关草案,但是其在生产环境被 Chrome 支持是在 2015 年。因此,如果我们把 PWA 的关键技术之一 Service Worker 的出现作为 PWA 的诞生时间,那就应该是 2015 年。
自 2015 年以来,PWA 相关的技术不断升级优化,在用户体验和用户留存两方面都提供了非常好的解决方案。PWA 可以将 Web 和 App 各自的优势融合在一起:渐进式、可响应、可离线、实现类似 App 的交互、即时更新、安全、可以被搜索引擎检索、可推送、可安装、可链接。
其中,App Manifest 让 Web 站点能被添加到手机桌面,解决了用户到达站点链条太长的问题;Service Worker 让 Web 站点能够离线状态下正常使用,还有能让用户离线接受站点消息推送的 Web Push,这两点非常值得关注。
而与此同时,如火如荼的 Native App 却没能很好地弥补自己的劣势。
对于 Native App 来说,其 最大的痛点是由于其天生封闭的基因,内容无法被索引,这会导致后续一系列的问题。例如,用户想知道红烧肉的做法,还需要先知道 App 的名字,下载 App 后才能获取内容,这个流程是十分不合理的。而随着移动互联网的发展,用户下载 App 的热情也逐渐减弱,再加上用户 80% 的时间被 Top3 的超级 App 占据,对于站点来说,应用分发成本也因此越来越高了。
相对于 Native App 的封闭,PWA 却是完全开放的——PWA 现有的所有技术都是遵循 W3C 的标准,完全开放,因此能够快速被站点接受、被浏览器快速支持。
值得一提的是,为了解开传统 Web 的两个枷锁,除 PWA 之外,业界也诞生了很多技术方案,例如部分厂商推出的小程序技术。
二、细解PWA:是否能真正弥补 Web 劣势?
PWA 不是特指某一项技术,而是应用了多项技术的 Web App。其核心技术包括 App Manifest、Service Worker、Web Push、Credential Management API ,等等。其核心目标就是提升 Web App 的性能,改善 Web App 的用户体验。
下面我们详细地看一下这些核心技术,是否能够真正弥补 Web 劣势。
Web App Manifest 是为了解决用户留存问题而诞生的,它是一个外链的 JSON 文件,在这个文件中,像浏览器暴露了站点的名称,地址,图标等等元数据,可以看下面这个例子。Web App Manifest 有很多配置项,MDN 的文档:
https://developer.mozilla.org/en-US/docs/Web/Manifest
{
"name": "百度天气",
"short_name": "天气",
"icons": [
{
"src": "/dist/static/img/icons/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
}
],
"start_url": "/?from=homescreen",
"background_color": "#3E4EB8",
"display": "standalone",
"theme_color": "#2F3BA2"
}
在浏览器中通过 <link rel="manifest" href="/manifest.json">
引入这个 JSON 文件,浏览器识别到这个文件的存在,会根据自己的机制决定是否弹出添加到桌面对话框,并在桌面上生成一个应用的图标,通过点击桌面图标进入应用具有沉浸式的体验,全屏显示,没有浏览器地址栏,并且还会自动添加应用启动画面,入下图所示。
图片来源于《下一代 Web 应用模型 — Progressive Web App》:
https://huangxuan.me/2017/02/09/nextgen-web-pwa/
Web App Manifest 当前也存在一些问题,比如 Web App Manifest 添加到桌面依赖于手机操作系统是否给浏览器开通在桌面创建快捷方式的权限,目前在国内,大部分情况下,浏览器的这个权限被默认禁止,需要用户手动开启,拿小米手机为例,可以进入设置 -> 更多应用 -> 找到对应的浏览器 -> 权限管理 -> 桌面快捷方式 -> 允许,对于大部分用户来说,这个操作可能过于复杂了,这也是 PWA 添加到桌面在国内还没有被广泛接受的主要原因,很多厂商,包括百度在内,都在尽力推动手机厂商开启这个权限。
其他 Web App Manifest 的详细资料可以参考 LAVAS 官网中 Web App Manifest 的文档:
https://lavas.baidu.com/doc/engage-retain-users/introduction
PWA 之所以能离线,是 Service Worker 的功劳。这并不是 W3C 第一次尝试让 Web 站点离线,在 Service Worker 之前,有个东西叫 Application Cache,<html manifest="cache.appcache">
,是不是很熟悉?但是,由于Application Cache 存在很多无法容忍和无法解决的问题(可以查看这篇博客 Application Cache is a Douchebag:https://alistapart.com/article/application-cache-is-a-douchebag),它在 HTML5.1 版本中被移出了。
现在,我们终于有一个没有那么多问题的离线方案了,那就是 Service Worker。
Service Worker 是一个特殊的 Web Worker,独立于页面主线程运行,它能够拦截和处理网络请求,并且配合 Cache Storage API,开发者可以自由的对页面发送的 HTTP 请求进行管理,这就是为什么 Service Worker 能让 Web 站点离线的原因。
Service Worker 的工作流程如下图所示。
Service Worker 的工作方式也衍生出了几种不同的请求控制策略,networkFirst
, cacheFirst
, networkOnly
, cacheOnly
和 fastest
,对于不同类型的请求,我们应该采取不同的策略,静态文件,我们可以选择 cacheFirst
或者 fastest
,甚至 cacheOnly
,对于依赖后端数据的 AJAX 请求,我们应该选择 networkFirst
或者 networkOnly
,保证数据的实时性。
Service Worker 从在浏览器注册到进入工作状态和最终销毁会经历不同的阶段,下图比较清楚的画出了 Service Worker 的生命周期。
在整个生命周期中,Service Worker 会抛出不同的事件,如 install
, active
, fetch
等,可以通过 self.addEventListener
来监听。
比如,监听网络请求事件,这里我们采用的策略是 cacheFirst
。
// service-worker.js
self.addEventListenr('fetch', (e) => {
e.respondWith(
caches.match(e.request).then((response) => {
return response || fetch(e.request);
})
);
});
这里只是简单的介绍了一下 Service Worker,详细的使用文档可以参考 怎么使用 Service Worker。
Service Worker 它并不只是能够缓存离线文件,后台同步和 Web Push 都是依赖于 Service Worker 工作的,因此它的重要性不言而喻。
Push Notification 其实是两个 API 的结合, Notification API 和 Push API 。
Notification API 提供了开发者可以给用户发送通知的能力,包括申请显示通知权限,发起通知,以及定制通知的类型等等。Notification API 的历史比较早,最早可以追溯到 2010 年,2011 年纳入标准,时至今日,Notication API 已经获得了大多数平台的支持,包括 Chrome, Edge, Firefox, Safari 等的支持,比较可惜的是,iOS Safari 至今还不支持。
Push API,则是让服务器能够向用户发送离线消息,即使用户当前并没有打开你的页面,甚至没有打开浏览器。浏览器在接到消息推送时,会唤醒对应的 Service Worker,并抛出 push
事件,开发者接收到事件之后调用 Notification API 弹出通知,这就完成了整个接受和展示的流程。
// service-worker.js
self.addEventListener('push', event => {
event.waitUntil(
// Process the event and display a notification.
self.registration.showNotification("Hey!")
);
});
self.addEventListener('notificationclick', event => {
// Do something with the event
event.notification.close();
});
self.addEventListener('notificationclose', event => {
// Do something with the event
});
在之前的分享和文章中,很少提及 Web Push,这并不是因为它不重要,而是因为它在国内被支持程度非常低,支持 Web Push 的成本比 App Manifest 或者 Service Worker 要高的多,它需要浏览器厂商提供消息推送服务,截止到本文截稿,国内只有 UC 即将发布的 U2 内核的浏览器才支持 Web Push API,Chrome 也因为其依赖的 FCM/GCM 无法访问而导致 Web Push 无法使用。
浏览器接收到离线消息需要完成两个过程:
浏览器订阅通知
服务器发送通知
浏览器订阅通知,是指开发者调用 API 在消息服务器注册,具体过程如下图所示,通过服务器提供的 Public Key 从浏览器获取 PushSubscription
对象,PushSubcription
包含浏览器对应的消息服务器的地址和一些密钥认证数据,将它发送到服务器,服务器将这些数据存储,发送离线消息需要使用到这些数据。订阅通知的过程就完成了。
服务器发送通知,服务器用 Private Key 将消息加密发送到之前保存的 PushSubcription
中对应的消息服务器,消息服务器解密消息后将消息推送到用户的浏览器,浏览器唤醒 Service Worker,这就完成了整个消息推送的过程。
除了 Web App Manifest、Service Worker、Push API 这三个关键的技术外,PWA 还包含很多优化的准则,比如 PRPL 模式,App Shell 模型,Credential Management API ,等等。PWA 不是某一种特定的技术,换句话来说,PWA 是采用各种技术达到站点用户体验非常好的 Web App。单单从技术上讲,已经能够很好地弥补传统 Web 的劣势了。
三、放眼未来:PWA 将带来新一轮前端技术洗牌?
各家浏览器厂商在 2017 年开始大力支持 PWA,下图统计了主流浏览器对 PWA 的支持程度,可以看到,大部分浏览器对 PWA 已经支持得很好了。
图片来源于 ispwaready
UC 浏览器开发的 U2 内核已经支持 Push API 了,也是国内第一个支持 Push API 的浏览器。现在浏览器厂商唯恐自己没跟上标准,而被淘汰。
不仅国内浏览器厂商的态度发生转变,连苹果都已经在几个月前悄悄的进行了 Service Worker 的开发了,相信在不远的将来,iOS 也将支持 PWA。
为了降低 PWA 的开发门槛,业界也推出了相应的工具。
例如,百度推出的 Lavas 就是一个开源的命令行工具,可以通过它来快速创建 PWA 项目。它提供了多种常用的 APP Shell 给用户选择,降低了开发成本,也简化了工作流程,让 PWA 项目变得易于管理。
PWA 刚推出时,就获得了很多大型站点的支持,比如印度最大的电商网站 Flipcart,它是第一个大规模应用 PWA 的站点,也取得了非常好的收益,用户停留时长增长了 3 倍。除 Flipcart 之外,还有很多不错的案例。下面我们来看看国内外的两个站点的实践案例。
案例1:Twitter Lite PWA
首先,国外的 Twitter 在 2017 年上线了 Twitter Lite PWA, Google 开发者网站上有 Twitter PWA 的案例分析。Twitter Lite PWA 同样收益惊人:
平均用户停留时长增长 65%
Web 站点发推的数量增长 75% (Amazing)
跳出率降低 20%
Twitter Lite 能取得这样的成绩,归功于 PWA 的新技术和用户体验至上的设计原则:它通过 Service Worker 缓存文件,让页面可以离线,同时降低网络消耗;通过 Web Push 接受服务器推送的消息;采用 App Shell 的设计模型,配合 Service Worker 能让页面瞬间展现。
案例2:ele.me PWA
Google 开发者网站上也对 ele.me 的案例进行了 分析。从这个案例分析中,我们可以看到 ele.me PWA 改造的收益如下:
预缓存的页面平均加载时间减少 11.6%
所有页面的平均加载时间减少 6.35%
在 3G 网络并且是第一次加载时,从页面加载到用户可操作的时间下降到 4.93s
可见,ele.me 同样取得了很不错的收益。不同于 Twitter Lite,ele.me 是 MPA 站点,这会让站点变的更复杂,并且体验不如 SPA 那么顺畅,但是 ele.me 充分利用了 PWA 的各种新技术和设计模式,将 MPA 的影响降到最小,比如使用了 PRPL 模式,最大程度的降低页面的首屏时间,还采用了 App Skeleton 的设计方式让用户对正在加载的页面内容有心里预期。
Twitter 和 ele.me 只是 PWA 站点中两个效果比较显著的案例,同样还有很多其他的案例,可以访问 Google 的案例分析页面合集:
https://developers.google.cn/web/showcase/
实际收益明显,再加上 Google 的强力支持,使得 PWA 的增长非常迅速,越来越多的互联网大站跟进。下面这张图列出了一些站点,从最开始的 Flipcart,到目前的 Instangram、Uber、Twitter、Starbucks 等,不仅数量在增加,站点等级和质量也在不断地提升。放眼国内,百度、饿了么、阿里都已经有部分站点支持 PWA 了,滴滴也表示有兴趣,可见,PWA 不仅在国外非常受重视,在国内同样受到各大互联网企业的重视。
按照当前的发展趋势,PWA 将会带来 Web App 的大量需求,新一轮大前端技术洗牌很可能近在眼前了。
作者简介
彭星,Brilliant Open Web 团队成员,百度资深前端工程师,负责百度在 PWA 方向上的工作,致力于提升 Web 的体验。
欢迎加入关注Brilliant Open Web技术群,与作者交流!加群方式:OpenWeb 开发者(ID:BrilliantOpenWeb),回复”加群“。
Brilliant Open Web
BOW(Brilliant Open Web)团队,是一个专门的Web技术建设小组,致力于推动 Open Web 技术的发展,让Web重新成为开发者的首选。
BOW 关注前端,关注Web;剖析技术、分享实践;谈谈学习,也聊聊管理。
OpenWeb开发者
ID:BrilliantOpenWeb
技术连接世界,开放赢得未来