【pwa】pwa学习笔记(一)

前言

  • pwa这门技术不火的原因还是很明显的,但是这个技术好处也是非常明显的,知道的人越多,这个技术越繁荣,就有更多人“挨饿”。由于这技术主要还是谷歌推,国内访问不了谷歌,这就导致国内学习pwa技术有点麻烦。资料也比较少,恰好翻到些文章,总结学习下。

安装

  • 首先最应该说的就是pwa的应用安装了。先看下显示条件:这个应用安装可以理解为网页的快捷入口,不过更像是应用而不是网页。原生应用就不用说了。

  • 显示应用安装横幅的条件:
    浏览器在 PWA 站点满足以下条件时会自动显示横幅:
    站点部署 manifest.json,该文件需配置如下属性:
    short_name (用于主屏幕显示)
    name (用于安装横幅显示)
    icons (其中必须包含一个 mime 类型为 image/png 的图标声明)
    start_url (应用启动地址)
    display (必须为 standalone 或 fullscreen)
    站点注册 Service Worker。
    站点支持 HTTPS 访问。
    站点在同一浏览器中被访问至少两次,两次访问间隔至少为 5 分钟。

  • 显示原生应用安装横幅的条件
    浏览器在 PWA 站点满足以下条件时会自动显示横幅:
    站点部署 manifest.json,该文件需配置如下属性:
    short_name (用于主屏幕显示)
    name (用于安装横幅显示)
    icons (其中必须包含一个 192x192 且 mime 类型为 image/png 的图标声明)
    包含原生应用相关信息的 related_applications 对象
    站点注册 Service Worker。
    站点支持 HTTPS 访问。
    站点在同一浏览器中被访问至少两次,两次访问间隔至少为 2 天。
    其中 related_applications 的定义如下:
    related_applications: Array. 关联应用列表
    AppInfo 的属性值包括:
    platform: {string} 应用平台
    id: {string} 应用id

代码

  • 主要还是依靠监听beforeinstallprompt事件。事件mdn
  • 在页面出现后,会触发这个事件,这个事件的event对象正常来说会显示横幅,当然也有支持不好的就不会显示横幅,需要主动触发,于是可以使用preventDefault先禁掉再通过prompt方法主动触发。
  • 我研究了下,这个event事件的prompt方法只能放到监听用户动作里,不然会报错,虽然报错也能工作。这个beforeinstallprompt事件,在用户拒绝安装后必须设置为空。等待下次触发。
  • 一般点击按钮安装应用:
      <button hidden id="installBtn">安装应用</button>
      <script>
        window.addEventListener('load', function () {
          if ('serviceWorker' in navigator) {
            navigator.serviceWorker.register('./sw.js');
          }
          let appPromptEvent = null;
          const installBtn = document.getElementById('installBtn');
          window.addEventListener('beforeinstallprompt', function (event) {
            console.log('触发事件:beforeinstallprompt');
            event.preventDefault();
            appPromptEvent = event;
            installBtn.hidden = false;
            return false;
          });
          installBtn.addEventListener('click', function () {
            if (appPromptEvent !== null) {
              console.log(appPromptEvent)
              appPromptEvent.prompt();
              appPromptEvent.userChoice.then(function (result) {
                if (result.outcome === 'accepted') {
                  console.log('同意安装应用');
                } else {
                  console.log('不同意安装应用');
                }
                appPromptEvent = null;
              });
            }
          });
          window.addEventListener('appinstalled', function () {
            console.log('应用已安装');
            installBtn.hidden = true;
          });
        });
      </script>
  • 这个后面的userChoice就有点像小程序里写的选择了。
  • 另外manifest.json忘说了。html里加入Link
  <link rel="manifest" href="./manifest.json">

manifest.json

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

Service Worker

  • 提到pwa,不得不学习Service Worker,相比于pwa,service Worker基本支持的很完善。

注册

  • 使用Service Worker得注册:
if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('./sw.js', { scope: './' }).then(function(registration) {
      // do domething...
    }).catch(function(err) {
      // do domething...
    });
  });
}
  • register 方法中的 scope 参数指定了 Service Worker 可接收 fetch 事件的作用域,比如 scope 的值为 /mobile,那么 Service Worker 便只能接收 path 以 /mobile 开头的 fetch 事件,默认值为 sw.js 所在路径。

安装

  • 注册完需要进行安装:
self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('sw-cache').then(function(cache) {
      return cache.addAll([
        '/',
        '/index.html',
        '/main.css',
        '/main.js',
        '/image.jpg'
      ]);
    })
  );
});
  • Service Worker进入安装需要有2个条件:
    页面中尚未安装 Service Worker。
    Service Worker 已安装,并且从服务器获取的 sw.js 文件与本地版本存在差异。
  • 这个caches是api。caches的mdn。下面会有基本使用。
  • waituntil是需要个promise作为参数,会等promise resolve继续下一阶段。

等待

  • 安装成功后,如果已经存在一个版本的 Service Worker 且有页面正在使用该版本,新版 Service Worker 便会进入等待状态,当 Service Worker 处于该阶段时,由于它必须等正在运行旧版本 Service Worker 的页面全部关闭后才会获得控制权,因此如果我们需要所有页面能够及时得到更新,可在 install 中通过 self.skipWaiting 来强制跳过该阶段。
self.addEventListener('install', function(event) {
  self.skipWaiting();
  //……
});

激活

  • 当满足以下任意条件,即可进入该状态:
    1 self.skipWaiting 方法被调用。
    2 安装完成后,不存在旧版本的 Service Worker 或无页面使用此版本。
    3 等待状态下正在运行旧版本 Service Worker 的页面被全部关闭(页面刷新或切换无法使 Service Worker 从等待进入激活状态,这是由于当页面刷新或切换时,浏览器需要等到新页面渲染完成之后才会销毁旧页面,即新旧两个页面存在共同的交叉时间)。
  • 进入该事件,会触发activate,常用来对缓存维护。
self.addEventListener('activate', function(event) {
  event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.filter(function(cacheName) {
          return cacheName != 'sw-cache';
        }).map(function(cacheName) {
          return caches.delete(cacheName);
        })
      );
    })
  );
});
  • 当 Service Worker 被首次注册时,已打开的页面只有在刷新后才会接受 Service Worker 的控制,如果想要 Service Worker 在激活后尽快掌握这些页面的控制权,可在 activate 中调用 self.clients.claim 方法来实现:
self.addEventListener('activate', function(event) {
  self.clients.claim()
  //……
});

已激活

  • 到这个时候,就可以通过监听 fetch、push、sync、message 等事件来为应用提供丰富的离线处理能力。

注销

  • 一般用不上,浏览器application里面有个serviceWorker可以点击unregister注销。
  • 或者代码:
const serviceWorker = navigator.serviceWorker;
if (typeof serviceWorker.getRegistrations === 'function') {
  serviceWorker.getRegistrations().then(function(registrations) {
    registrations.forEach(function(registration) {
      registration.unregister();
    });
  });
} else if (typeof serviceWorker.getRegistration === 'function') {
  serviceWorker.getRegistration().then(function(registration) {
    registration.unregister();
  })
}
  • 一个有s一个没s,复数循环注销,单数直接注销。

状态监听

  • 注册成功后,我们可通过回调中的 registration 参数来获取以下状态的 ServiceWorker 实例。
  • 实例可以监听各种状态,比如updatefound等。mdn可监听状态
navigator.serviceWorker.register('./sw.js').then(function(registration) {
  registration.addEventListener('updatefound', function() {
  });
});
  • 取得控制权的监听:
navigator.serviceWorker.addEventListener('controllerchange', function() {
});

IndexedDB

  • 由于serviceWorker只能获取indexDB或者caches内容,所以这2个基本使用也是要学习的。有网址的使用caches,其他资源使用IndexedDB。
  • 打开数据库,第二个参数是版本号。IndexedDBmdn
const openRequest = window.indexedDB.open('TodoList', 1);
  • indexedDB操作都是异步调用的,所以创建数据库是通过onupgradeneeded获得event回调来做:
openRequest.onupgradeneeded = function(event) {
  const db = event.target.result;
  const todosStore = db.createObjectStore('todos', { keyPath: 'id', autoIncrement : true });
  todosStore.createIndex('status', 'status');
};
openRequest.onsuccess = function(event) {
  const db = event.target.result;
  const transaction = db.transaction(['todos'], 'readonly');
  const todosStore = transaction.objectStore('todos');
  // ……
};
  • 这个是基于事务来操作,三种事务模式:readonly,readwrite,和versionchange。
var transaction = db.transaction(["customers"], "readwrite");
db.transaction("customers").objectStore("customers").get("444-44-4444").onsuccess = function(event) {
  alert("Name for SSN 444-44-4444 is " + event.target.result.name);
};
var request = db.transaction(["customers"], "readwrite")
                .objectStore("customers")
                .delete("444-44-4444");
request.onsuccess = function(event) {
  // 删除成功!
};

Caches

  • 先通过CacheStorage获取对象,然后通过Cache接口进行操作。

  • CacheStorage 接口
    主要接口为:
    open:获取指定名称的 Cache 对象。
    keys:获取 CacheStorage 所有 Cache 对象中的 Response 条目键值列表。
    has:判断是否存在指定名称的 Cache 对象。
    delete:删除指定名称的 Cache 对象。
    match:获取指定请求所对应的 Response 条目(如匹配到多个,则返回第一个)。

  • Cache 接口
    match:获取指定请求所对应的 Response 条目(如匹配到多个,则返回第一个)。
    matchAll:与 match 的唯一差别是该接口返回所有匹配项。
    add:获取指定 URL 的资源,将返回的 Response 添加到 Cache 对象中。
    addAll: 与 add 的唯一差别是该接口可以获取多个 URL 的资源,并将其依次添加到 Cache 对象中。
    put:将指定 Request 的 Response 添加到 Cache 对象中。
    delete: 删除指定 Request 的 Response 条目。
    keys:获取指定 Request 的 Response 条目键值列表。

发布了178 篇原创文章 · 获赞 11 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/yehuozhili/article/details/105273387
PWA