PWA初探整理

关键特性

  • Web App Manifest – 在主屏幕添加app图标,定义手机标题栏颜色之类
  • App Shell – 先显示APP的主结构,再填充主数据,更快显示更好体验
  • Service Worker - 缓存,离线开发,以及地理位置信息处理等
  • Push Notifion - 消息推送

Service Worker

运行在浏览器端的代理服务器

clipboard.png

基本特点

  • 事件驱动型服务线程
  • 只能基于https或者localhost
  • 可以控制页面所发送网络请求的处理方式
  • 运行在浏览器后台的脚本,无法直接操作dom

生命周期

clipboard.png

支持事件

clipboard.png

register

  • 在主线程代码中注册
  • 可以指定scope,通常指定到网站根路径,能够拦截所有的fetch事件
  • service worker 已经被注册过,浏览器会自动忽略上面的代码
if ('serviceWorker' in navigator) {
  window.addEventListener('load', function () {
    navigator.serviceWorker.register('/sw.js', {
      scope: '/'
    }).then(function (reg) {
      // 注册成功
      console.log('success and scope: ', reg.scope);
    }).catch(function (err) {
      // 注册失败
      console.log('failed: ', err);
    });
  });
}

installing

缓存文件

installed/waiting

此状态下,worker有效但尚未激活,它尚未纳入 document的控制,确切来说是在等待着从当前 worker 接手

sw.js发生了更新,但是页面一直挂载中,没有关闭或强制刷新,此时上一个版本的sw还在工作中,新的sw处于等待中

// 安装阶段跳过等待,直接进入 active
self.addEventListener('install', function (event) {
  event.waitUntil(self.skipWaiting());
});

self.addEventListener('activate', function (event) {
  event.waitUntil(
    Promise.all([

      // 更新客户端
      self.clients.claim()

    ])
  );
});

activating/activated

缓存更新

fetch

代理请求

message

sw与主线程间的双向通信,建立MessageChannel作为纽带


// index.html
navigator.serviceWorker.register('/service-worker.js', {
  scope: '/'
}).then(function (reg) {
  // 创建一个
  const channel = new MessageChannel();
  
  // port1供主线程使用
  channel.port1.onmessage = messageEvent => {
    console.log('来自sw的message', messageEvent.data);
  }
  
  const serviceWorker = reg.active;
  
  // port2指向sw
  if (serviceWorker) {
    serviceWorker.postMessage('index->sw', [channel.port2]);
  }
});

// sw.js
self.addEventListener('message', function (event) {
  console.log('来自index的message', event.data);
  // 通过port找到发送消息的client
  event.ports[0].postMessage('sw->index');
});

更多的应用场景

  • 后台数据同步
  • 预取用户可能需要的资源,比如相册中的后面数张图片
  • 在后台集中接收计算成本高的数据更新,比如地理位置和陀螺仪信息,多个页面就可以利用同一组数据

Cache API

  • 只能缓存 GET 请求;
  • 可以缓存属于自己域下的请求,同时也可以缓存跨域的请求,不过无法对跨域请求的Request和Response进行修改
  • 缓存的更新需要自行实现
  • 缓存不会过期,除非将手动删除,大小有限制,LRU删除

caches.open

创建一个cache

cache.add/addAll

  • 支持传入Requesturl
  • 缓存资源,支持单个和数组
  • 在cache.add内部会自动去调用fetch取回request的请求结果,然后才是把response存入cache

cache.put

  • 相当于cache.add的第二步,即fetch到response后存入cache
  • 无法直接缓存跨域的请求,response.status会返回0

    • 如果跨域的资源支持CORS,那么可以把request的mod改成cors

caches.match

catch.match(request, {
  
}).then(function(response) {

})

cache.delete

一些使用点

  • 分段缓存,提高安装成功率

    • 先安装非重要资源,再安装重要资源
  • 渐进式缓存

    • 对于在install时没有缓存的资源,可以在用户交互之后再缓存
  • 优先原则

    • 对于静态页面,缓存优先,减少请求
    • 对于天气类型应用,先去fetch,服务器故障或者网络不良时,折回本地缓存

Manifest

一个基本的manifest.json

{
    "short_name": "短名称",
    "name": "这是一个完整名称",
    "icon": [
        {
            "src": "icon.png",
            "type": "image/png",
            "sizes": "48x48"
        }
    ],
    "start_url": "index.html"
}

可以实现的功能

  • 基本功能

    • 自定义名称
    • 自定义图标
    • 设置启动网址
    • 设置作用域
  • 添加启动画面

    • 设置显示类型
    • 指定显示方向
    • 设置主题色
  • 应用安装横幅

    • 站点部署 manifest.json,该文件需配置如下属性:

      • short_name (用于主屏幕显示)
      • name (用于安装横幅显示)
      • icons (其中必须包含一个 mime 类型为 image/png 的图标声明)
      • start_url (应用启动地址)
      • display (必须为 standalone 或 fullscreen)
    • 站点注册 Service Worker。
    • 站点支持 HTTPS 访问。
    • 站点在同一浏览器中被访问至少两次,两次访问间隔至少为 5 分钟。

Web Push

Notification

PushManager

clipboard.png

  • 询问授权
  • 发送subscription给后端存储
  • 服务端向FCM/GCM发送消息,同时带上subscription
  • FCM根据subscription再下发给对应的浏览器
  • 触发Service Worker的push事件
  • 后续处理

参考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章