渐进式网页应用(Progressive Web Apps,PWA)是一种使用现代 Web 技术构建的应用,它能够提供类似于原生应用体验的网页应用。PWA 核心的理念是渐进性增强,即在基本的网页功能基础上逐步添加现代 Web 特性,使得网页应用能在任何设备和任何浏览器上运行,并且在网络环境较差或者离线状态下仍可使用。
PWA 通常具备以下特点:
响应式设计:
PWA 网页设计上通常是响应式的,意味着它们能够适配各种屏幕尺寸和设备。离线工作:
使用 Service Workers,PWA 能够在没有网络连接的情况下工作,提供核心内容和功能。类似应用的交互:
PWA 在交互上更近似于原生应用,包括滑动手势、平滑的动画和无需页面刷新的导航体验。安全性:
PWA 通常通过 HTTPS 协议提供服务,确保数据传输的安全性。易于发现:
由于 PWA 本质上是网站,它们能够被搜索引擎检索到,比原生应用更容易被用户发现。可安装:
用户可以将 PWA 添加到手机主屏幕,不需要通过应用商店进行下载。自动更新:
PWA 可以自动更新,用户无需手动更新应用。推送通知:
PWA 可以使用 Web 推送技术发送推送通知,提高用户参与度。链接可共享:
PWA 的页面可以使用普通网址进行分享,用户之间分享链接时可直接访问应用的特定内容。
技术要点:
以下是构建 PWA 时通常需要的一些关键技术和特性:
- Service Workers:一种运行在 Web 应用后台的脚本,能够拦截和缓存网络请求,提供离线访问能力。
- Web App Manifest:一个 JSON 文件,描述了网页应用的名称、图标、起始 URL、背景色等元数据,使得 PWA 可被添加到主屏幕。
- 应用壳(App Shell)模型:设计 PWA 时的一种方式,目的是快速加载应用的核心 UI 框架,然后动态加载内容。
- 推送通知 API:允许应用接收和显示系统级的推送通知。
Web 应用 VS 本地应用
那相对于本地应用,Web 页面到底缺少了什么?
- 首先,Web 应用缺少离线使用能力,在离线或者在弱网环境下基本上是无法使用的。而用户需要的是沉浸式的体验,在离线或者弱网环境下能够流畅地使用是用户对一个应用的基本要求。
- 其次,Web 应用还缺少了消息推送的能力,因为作为一个 App 厂商,需要有将消息送达到应用的能力。
- 最后,Web 应用缺少一级入口,也就是将 Web 应用安装到桌面,在需要的时候直接从桌面打开 Web 应用,而不是每次都需要通过浏览器来打开。
针对以上 Web 缺陷,PWA 提出了两种解决方案:通过引入 Service Worker 来试着解决离线存储和消息推送的问题,通过引入 manifest.json 来解决一级入口的问题。下面我们就来详细分析下 Service Worker 是如何工作的。
什么是 Service Worker
我们先来看看 Service Worker 是怎么解决离线存储和消息推送的问题。
其实在 Service Worker 之前,WHATWG 小组就推出过用 App Cache 标准来缓存页面,不过在使用过程中 App Cache 所暴露的问题比较多,遭到多方吐槽,所以这个标准最终也只能被废弃了,可见一个成功的标准是需要经历实践考量的。
所以在 2014 年的时候,标准委员会就提出了 Service Worker 的概念,它的主要思想是在页面和网络之间增加一个拦截器,用来缓存和拦截请求。整体结构如下图所示:
在没有安装 Service Worker 之前,WebApp 都是直接通过网络模块来请求资源的。安装了 Service Worker 模块之后,WebApp 请求资源时,会先通过 Service Worker,让它判断是返回 Service Worker 缓存的资源还是重新去网络请求资源。一切的控制权都交由 Service Worker 来处理。
Service Worker 的设计思路
现在我们知道 Service Worker 的主要功能就是拦截请求和缓存资源,接下来我们就从 Web 应用的需求角度来看看 Service Worker 的设计思路。
1. 架构
通过前面页面循环系统的分析,我们已经知道了 JavaScript 和页面渲染流水线的任务都是在页面主线程上执行的,如果一段 JavaScript 执行时间过久,那么就会阻塞主线程,使得渲染一帧的时间变长,从而让用户产生卡顿的感觉,这对用户来说体验是非常不好的。
为了避免 JavaScript 过多占用页面主线程时长的情况,浏览器实现了 Web Worker 的功能。Web Worker 的目的是让 JavaScript 能够运行在页面主线程之外,不过由于 Web Worker 中是没有当前页面的 DOM 环境的,所以在 Web Worker 中只能执行一些和 DOM 无关的 JavaScript 脚本,并通过 postMessage 方法将执行的结果返回给主线程。所以说在 Chrome 中, Web Worker 其实就是在渲染进程中开启的一个新线程,它的生命周期是和页面关联的。
“让其运行在主线程之外” 就是 Service Worker 来自 Web Worker 的一个核心思想。不过 Web Worker 是临时的,每次 JavaScript 脚本执行完成之后都会退出,执行结果也不能保存下来,如果下次还有同样的操作,就还得重新来一遍。所以 Service Worker 需要在 Web Worker 的基础之上加上储存功能。
另外,由于 Service Worker 还需要会为多个页面提供服务,所以还不能把 Service Worker 和单个页面绑定起来。在目前的 Chrome 架构中,Service Worker 是运行在浏览器进程中的,因为浏览器进程生命周期是最长的,所以在浏览器的生命周期内,能够为所有的页面提供服务。
2. 消息推送
消息推送也是基于 Service Worker 来实现的。因为消息推送时,浏览器页面也许并没有启动,这时就需要 Service Worker 来接收服务器推送的消息,并将消息通过一定方式展示给用户。关于消息推送的细节这里我们就不详述了,如果你感兴趣的话可以自行搜索相关资料去学习。
3. 安全
基于 Web 应用的业务越来越多了,其安全问题是不可忽视的,所以在设计 Service Worker 之初,安全问题就被提上了日程。
关于安全,其中最为核心的一条就是 HTTP。我们知道,HTTP 采用的是明文传输信息,存在被窃听、被篡改和被劫持的风险,在项目中使用 HTTP 来传输数据无疑是 “裸奔”。所以在设计之初,就考虑对 Service Worker 采用 HTTPS 协议,因为采用 HTTPS 的通信数据都是经过加密的,即便拦截了数据,也无法破解数据内容,而且 HTTPS 还有校验机制,通信双方很容易知道数据是否被篡改。关于 HTTPS 协议,我们会在最后的安全模块详细介绍。
所以要使站点支持 Service Worker,首先必要的一步就是要将站点升级到 HTTPS。
除了必须要使用 HTTPS,Service Worker 还需要同时支持 Web 页面默认的安全策略,诸如同源策略、内容安全策略(CSP)等,关于这些,后续我们也会详细介绍。
总结
好了,今天就介绍到这里,下面我来总结下本文的主要内容。
我们先分析了 PWA,它是由很多技术组成的一个理念,其核心思想是渐进式。对于开发者,它提供了非常温和的方式,让开发者将普通的站点逐步过渡到 Web 应用。对于技术本身而言,它是渐进式演进,逐渐将 Web 技术发挥到极致的同时,也逐渐缩小和本地应用的差距。在此基础上,我们又分析了 PWA 中的 Service Worker 的设计思路。
另外,PWA 还提供了 manifest.json 配置文件,可以让开发者自定义桌面的图标、显示名称、启动方式等信息,还可以设置启动画面、页面主题颜色等信息。关于 manifest.json 的配置还是比较简单的,详细使用教程网上有很多,这里我就不做介绍了。
添加桌面标、增加离线缓存、增加消息推送等功能是 PWA 走向设备的必备功能,但我认为真正决定 PWA 能否崛起的还是底层技术,比如页面渲染效率、对系统设备的支持程度、WebAssembly 等,而这些技术也在渐进式进化过程中。所以未来如何,我们拭目以待。
总之,PWA 是将传统网页与原生应用体验结合的开发模式,它利用了 Web 平台的许多优势,为用户带来更好的体验,同时简化了开发者的发布和维护工作。随着 Web 技术的不断发展,PWA 的功能和性能正在逐渐接近原生应用。