PWA 的探索与最佳实践
PWA 是在 Web 端具有颠覆性的一个概念,在国外已经被普遍接受,很多站点已经改造成 PWA,并且取得了非常好的成绩。例如 twitter 改造 PWA 完成后,twitter lite 平均用户停留时长增长了65%。在国内,PWA 的概念越来越被广泛接受,微博、饿了么等很多大型的站点都已经改造成 PWA,并且还有更多的站点正在进行改造。
PWA 是什么
对于 PWA 来说,用户体验才是核心。PWA 不是一项具体的技术,他是应用了一系列技术进行使用体验优化后的Web APP,具有与Native App 一致的用户体验,能够添加主屏图标、离线可用、接收离线通知等。具体详情查看 Demo 演示。
PWA 具有三个主要的特性
可靠 一方面是指 PWA 的安全性,PWA 只能运行在 HTTPS 上;另一方面是指在网络不稳定或者没网情况下,PWA 依然可以访问。
快速 快速响应用户的交互行为,并且具有平滑流畅的动画、加载速度、渲染速度和渲染性能等。
用户粘性 通过添加到桌面以及离线消息推送,能带来用户的第二次访问,并且依靠良好的用户体验吸引用户再次访问。
PWA 的核心技术
PWA 不是一项单独的技术,技术包括 Web App Manifest、Service Worker、Push API & Notification API 、App Shell & App Skeleton 等等技术,接下来我们重点介绍几项技术以及相关问题的解决方法。
Web App Manifest
Web App Manifest 是支持站点在主屏上创建图标的技术方案,并且定制 PWA 的启动画面的图标和颜色等,如下图:
Web App Manifest 功能虽然强大,但是技术上并不难,就是一个外链的 json 文件,通过 link 来引入:<link rel="manifest" href=“/assets/manifest.json”>,文件的具体内容如下:
Web App Manifest 的标准里还有很多其他的字段,可以去 W3C 的标准里查找
https://w3c.github.io/manifest
Service Worker
Service Worker 是 PWA 中最重要的概念之一,它是一个特殊的 Web Worker,独立于浏览器的主线程运行,特殊在它可以拦截用户的网络请求,并且操作缓存,还支持 Push 和后台同步等功能。
Service Worker 通过 Cache Storage 、Cache API 操作本地缓存,以及通过 fetch API 请求服务器端数据,不管是否有网络连接,或者站点发生了 404 、500,都可以让用户看到特殊定制的错误页面,而不是浏览器的默认 404 页面。
App Shell 和 App Skeleton
PWA 通常是 SPA 且通常采用 App Shell 设计模型。App Shell 在 PWA 里是非常重要的一个概念,那么 App Shell 是什么呢?
App Shell 是指支持页面所需的最小的 HTML、CSS 和 JavaScript 的资源集合。一旦离线,可以确保在用户重复访问时提供即时、可靠的良好性能,下面的截图是 App Shell 展现给用户的部分
以 Vue 的项目举例,AppShell 包含:
入口 HTML 文件
打包好的 Vendor JS 文件
导出的 CSS 文件
如上图所示,App Shell 渲染出了 header 部分,那么正文部分在加载数据之前,都是白屏,这对于用户来说体验非常不好,有一个名词叫骨架屏(App Skeleton),在渲染出数据之前,在白屏位置占位,尽量不出现长时间的白屏。
App Skeleton 需要在最短的时间内渲染给用户,所以,一般情况下,会将 App Skeleton 编译到 HTML 里,就像下面的代码,期望浏览器在加载完 HTML 之后就能先显示骨架屏。
但是<link rel=“stylesheet” href=“/static/index.css”> 会阻塞骨架屏的渲染,直到 CSS 文件加载完成之后,浏览器才会渲染出骨架屏,这样用户看到的白屏时间会比预想中的长。这个问题很少有站点会注意到,即使做了骨架屏,也不一定会解决 CSS 加载阻止骨架屏渲染的问题,比如饿了么的 PWA 首页就没解决。
那么,我们怎么解决这个问题呢,可以通过 link 的 preload 解决,preload 不会阻止 HTML 渲染,在 preload 的资源加载完成之后,改回 stylesheet,调用 mount 来展现 JS 的渲染结果。如下面的代码所示(下面的代码并不能直接运行,只用来表明思路)
除了通过 preload 的这种方式解决之外,还可以将 link 里的 CSS 内联到 JS 里面,Vue 的项目默认就是 CSS In JS 的。
PWA 全称是 Progressive Web Apps,意味着是渐进式的,也就是在现有的基础上进行逐渐添加,从而改善用户体验,并不需要推倒重来,对整个站点进行改造。
PWA SEO 问题解决方案
SEO 是每个站点都很关心的问题,PWA 通常是 SPA,众所周知,传统的搜索引擎是无法索引通过 JS 来渲染的页面的,那么,我们需要怎么解决这个问题?
首先需要说明的是百度不是那个传统的搜索引擎,百度、Google 都是能够索引移动端的 SPA 页面的。那么对于其他搜索引擎,解决 PWA SEO 的问题唯一方法就是服务器端渲染(SSR),现在市面上主流的 MVVM 框架都有提供 SSR 的解决方案,比如 React、Vue、San 等。
SSR 的页面是服务器直出的,内容页是同时出来的,那么这种方式如何结合 Service Worker 来离线呢?如何做到 AppShell 的启动效果呢?我们采用 Service Worker + App Shell + SSR + (Vue/React/San) 的方案来非常完美的解决这个问题。
第一次请求,服务器直接输出 SSR 之后的内容,页面加载完成之后注册 Service Worker,Service Worker 在 install 阶段预缓存一些静态文件或者其他资源,在这里我们新增一个请求,地址是 /appshell,这个 /appshell 的请求返回的内容是 App Shell 的 HTML,这个 HTML 有引用 JS 和 CSS 的代码,可以举一个 Vue 的例子:
https://github.com/lavas-project/lavas-template-vue/blob/release-basic/pages/Appshell.vue
在第二次请求的时候,Service Worker 通过request.mode === ‘navigate’ 来判断当前这个请求是否是页面请求,如果是,将事先缓存好的 /appshell HTML 结构返回给页面,渲染之后,App Shell 通过当前 URL 去请求对应的 JS 和数据来渲染页面的正文内容。
通过这种方式,不仅能够解决 SEO 的问题,站点能完全离线,还能缩短白屏时间,更重要的一点是它还能大大降低服务器 SSR 带来的压力。因为用户只有第一次渲染才需要服务器运行 SSR 的逻辑,之后的请求都是走的前端渲染。
PWA 的兼容性
PWA 在 2017 年初,仅仅 Chrome 和 Firefox 支持 PWA,经过一年的发展,国内主流浏览器都已经支持 PWA,iOS 在 新发布的11.3 版本中也支持了 PWA。