深入浅出 PWA

PWA 全称为 Progressive Web App,中文译为渐进式 Web APP。PWA本质上是 Web 应用,使用现代 API 构建和增强,以提供增强的功能、可靠性和可安装性,同时只需一个代码库就可以借助任何设备触及任何用户、任何地方,实现与原生 App 相近的用户体验。

一个 PWA 首先是一个网页, 可以通过 Web 技术编写出一个网页应用,随后添加上 App Manifest 实现添加至设备主屏幕, 通过 Service Worker 来实现离线缓存和消息推送等功能。

Web Application Manifest

深入浅出 PWA_第1张图片

Web Application Manifest,即通过一个清单文件向浏览器暴露 web 应用的元数据,包括名称、icon 的 URL 等,以备浏览器使用,比如在添加至主屏或推送通知时暴露给操作系统,从而增强 web 应用与操作系统的集成能力。

一个典型的 manifest.json:

/** 属性含义详解: https://developer.mozilla.org/zh-CN/docs/Web/Manifest */
{
  "lang": "en",
  "dir": "ltr",
  "name": "Super Racer 3000",
  "short_name": "Racer3K",
  "icons": [{
    "src": "icon/lowres.webp",
    "sizes": "64x64",
    "type": "image/webp"
  }, {
    "src": "icon/lowres.png",
    "sizes": "64x64"
  }, {
    "src": "icon/hd_hi",
    "sizes": "128x128"
  }],
  "scope": "/",
  "id": "superracer",
  "start_url": "/start.html",
  "display": "fullscreen",
  "orientation": "landscape",
  "theme_color": "aliceblue",
  "background_color": "red"
}

通过对 manifest.json 进行相应配置,可以实现以下功能:
深入浅出 PWA_第2张图片

Service Worker

深入浅出 PWA_第3张图片

Service Worker 是一个可编程的 Web Worker,它就像一个位于浏览器与网络之间的客户端代理,可以拦截、处理、响应流经的 HTTP 请求;配合随之引入 Cache Storage API,可以自由管理 HTTP 请求文件粒度的缓存,这使得 Service Worker 可以从缓存中向 web 应用提供资源,即使是在离线的环境下。

Service workers 主要是提供详细的浏览器和网络/缓存间的代理服务,如下图所以:

深入浅出 PWA_第4张图片

Service workers 的生命周期:

深入浅出 PWA_第5张图片HTTP 缓存与 Service Worker 缓存 的区别:

  • HTTP 缓存中,Web 服务器可以使用 Expires 首部来通知 Web 客户端,它可以使用资源的当前副本,直到指定的“过期时间”。反过来,浏览器可以缓存此资源,并且只有在有效期满后才会再次检查新版本。 使用 HTTP 缓存意味着你要依赖服务器来告诉你何时缓存资源和何时过期(当然,HTTP 缓存控制还包括 cache-control,last-modified,etag 等字段)。

  • Service Workers 的强大在于它们拦截 HTTP 请求的能力,接受任何传入的 HTTP 请求,并决定想要如何响应。在你的 Service Worker 中,可以编写逻辑来决定想要缓存的资源,以及需要满足什么条件和资源需要缓存多久。一切尽归你掌控!(所以,出于安全考虑,Service Workers 要求只能由 Https 承载)

Service Worker 的注意事项:

  • Service worker 运行在 worker 上下文(self) –> 不能访问 DOM(这里其实和 Web Worker 是一样的);
  • 它设计为完全异步,同步 API(如 XHR 和 localStorage)不能在 service worker 中使用;
  • 出于安全考量,Service workers 只能由 HTTPS 承载;
  • 某些浏览器的用户隐私模式,Service Worker 不可用;
  • 其生命周期与页面无关(关联页面未关闭时,它也可以退出,没有关联页面时,它也可以启动)。

Service Worker 缓存优先的示意图:

深入浅出 PWA_第6张图片

深入浅出 PWA_第7张图片

Service workers 所支持的事件:

深入浅出 PWA_第8张图片

通常遵循以下基本步骤来使用 service workers:

  1. service worker URL 通过 serviceWorkerContainer.register() 来获取和注册。
  2. 如果注册成功,service worker 就在 ServiceWorkerGlobalScope 环境中运行; 这是一个特殊类型的 worker 上下文运行环境,与主运行线程(执行脚本)相独立,同时也没有访问 DOM 的能力。
  3. service worker 现在可以处理事件了。
  4. 受 service worker 控制的页面打开后会尝试去安装 service worker。最先发送给 service worker 的事件是安装事件(在这个事件里可以开始进行填充 IndexDB和缓存站点资源)。这个流程同原生 APP 或者 Firefox OS APP 是一样的 — 让所有资源可离线访问。
  5. 当 oninstall 事件的处理程序执行完毕后,可以认为 service worker 安装完成了。
  6. 下一步是激活。当 service worker 安装完成后,会接收到一个激活事件(activate event)。 onactivate 主要用途是清理先前版本的 service worker 脚本中使用的资源。
  7. Service Worker 现在可以控制页面了,但仅是在 register()  成功后的打开的页面。也就是说,页面起始于有没有 service worker ,且在页面的接下来生命周期内维持这个状态。所以,页面不得不重新加载以让 service worker 获得完全的控制。

PWA 实现

深入浅出 PWA_第9张图片

一个简单的 PWA demo 很简单,新建项目目录,然后:

touch index.html
touch sw.js
npm install serve -g

之后进行简单的 html 和 sw.js 文件的编写:

/** login.html */


  
    
    
    
    
    
    
    PWA
  

  
    

/** sw.js */ const CACHE_NAME = 'cache-v1'; self.addEventListener('install', (event => { console.log('---------install-----------', event); // event.waitUntil(self.skipWaiting()); event.waitUntil( caches.open(CACHE_NAME) .then(cache => { // 资源列表,不要人工获取 cache.addAll([ '/', './index.css' ]) }) ); })); self.addEventListener('activate', (event => { fetch('./userInfo.json', { method: 'post', body: { a: 1, b: 2 } }) console.log('--------activate----------', event); // event.waitUntil(self.clients.claim()); event.waitUntil(caches.keys().then(cacheNames => { return Promise.all(cacheNames.map(cacheName => { if (cacheName !== CACHE_NAME) { return cacheNames.delete(cacheName); } })) })) })); self.addEventListener('fetch', (event => { console.log('--------fetch---------', event); event.respondWith( caches.open(CACHE_NAME).then(cache => { return cache.match(event.request).then(response => { if (response) { return response; } // https://stackoverflow.com/questions/68522967/failed-to-execute-put-on-cache-request-method-post-is-unsupported-pwa-s if ((event.request.url.indexOf('http') === 0)) { return fetch(event.request).then(response => { // Check if we received a valid response if (!response || response.status !== 200 || response.type !== 'basic') { return response; } if (!event.request.url.endsWith('styles.css')) { // IMPORTANT: Clone the response. A response is a stream // and because we want the browser to consume the response // as well as the cache consuming the response, we need // to clone it so we have two streams. console.log('------event.request------', event.request) const responseToCache = response.clone(); cache.put(event.request, responseToCache); } return response; }) } }) }) ) })) 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 }); /** manifest.json */ { "name": "Progressive Web App", "short_name": "PWA", "description": "Progressive Web App.", "icons": [ { "src": "/icon.png", "sizes": "288x288", "type": "image/png" } ], "start_url": "/", "display": "standalone", "theme_color": "#B12A34", "background_color": "#B12A34" }

App Shell 模型 是构建 Progressive Web App 的一种方式,这种应用能可靠且即时地加载到用户屏幕上,与本机应用相似。App“shell”是支持用户界面所需的最小的 HTML、CSS 和 JavaScript,如果离线缓存,可确保在用户重复访问时提供即时、可靠的良好性能。这意味着并不是每次用户访问时都要从网络加载 App Shell,只需要从网络中加载必要的内容。对于使用包含大量 JavaScript 的架构的单页应用来说,App Shell 是一种常用方法。这种方法依赖渐进式缓存 Shell(使用 Service Worker 线程)让应用运行,接下来,为使用 JavaScript 的每个页面加载动态内容。App Shell 非常适合用于在没有网络的情况下将一些初始 HTML 快速加载到屏幕上。

PWA 安装后,就会出现在桌面/Chrome 应用里面:

深入浅出 PWA_第10张图片

 支持卸载:

深入浅出 PWA_第11张图片

深入浅出 PWA_第12张图片

PWA 的优劣势:

深入浅出 PWA_第13张图片

尽管有上述的一些缺点,PWA 技术仍然有很多可以借鉴和使用的点:

  • Service Worker 技术实现离线缓存,可以将一些不经常更改的静态文件放到缓存中,提升用户体验。
  • Service Worker 实现消息推送,使用浏览器推送功能,吸引用户
  • 渐进式开发,尽管一些浏览器暂时不支持,可以利用上述技术给使用支持浏览器的用户带来更好的体验

总结

你可能感兴趣的:(前端,PWA)