pwa

304 阅读3分钟

Progressive Web Application,全称“渐进式网页应用”,让你的网页,可以通过某种方式达到离线使用,可以让网页做到和app功能类似的断网链接,优化用户体验

听起来很神奇,今天做一个简单介绍
使用这项技术,需要2个关键性的文件(manifest.jsonsw.js

1. manifest.json

其实就是配置文件,主要介绍文件的图标,文件名称等(谷歌的浏览器插件也是这个套路)

v2-63ed4483cede84d0bd3ef1895875afcb_1440w.png
ios 中的 Safari 有一些特殊,单独配置

ios meta/link 私有属性设置

图标icon
<link rel="apple-touch-icon" href="apple-touch-icon-iphone.png"/> 
添加到主屏后的标题 和 short_name一致 
<meta name="apple-mobile-web-app-title" content="标题">
隐藏safari地址栏 standalone模式下默认隐藏
<meta name="apple-mobile-web-app-capable" content="yes" />
设置状态栏颜色 
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />

service worker

特点

  1. 不能访问/操作 dom,毕竟是开了一个新的线程
  2. 离线缓存内容开发者可控
  3. 必须在https或者localhost 使用
  4. 使用 api 都是基于Promise

生命周期

sevice worker 也有和 vue 类似的生命周期

  • 安装( installing ):这个状态发生在 Service Worker 注册之后,表示开始安装,触发 install 事件回调指定一些静态资源进行离线缓存。
  • 安装后( installed ):Service Worker 已经完成了安装,并且等待其他的 Service Worker 线程被关闭。
  • 激活( activating ):在这个状态下没有被其他的 Service Worker 控制的客户端,允许当前的 worker 完成安装,并且清除了其他的 worker 以及关联缓存的旧缓存资源,等待新的 Service Worker 线程被激活。
  • 激活后( activated ):在这个状态会处理 activate 事件回调 (提供了更新缓存策略的机会)。并可以处理功能性的事件 fetch (请求)、sync (后台同步)、push (推送)。
  • 废弃状态 ( redundant ):这个状态表示一个 Service Worker 的生命周期结束。

serviceWorker中的方法

  • self.skipWaiting():表示强制当前处在 waiting 状态的 Service Worker 进入 activate 状态
  • event.waitUntil():传入一个 Promise 为参数,等到该 Promise 为 resolve 状态为止。
  • self.clients.claim():在 activate 事件回调中执行该方法表示取得页面的控制权, 这样之后打开页面都会使用版本更新的缓存。旧的 Service Worker 脚本不再控制着页面,之后会被停止。

实现浏览器离线缓存的功能

window.addEventListener('load',function(){ 
// 页面加载完成后 注册serviceWorker 
 if('serviceWorker' in navigator){
  navigator.serviceWorker.register('./sw.js').then(registeration=>{ 
 console.log(registeration.scope);
}); 

navigator.serviceWorker.addEventListener('controllerchange',()=>{ console.log('change') }) } 

if(!navigator.onLine){ window.addEventListener('online',()=>{ console.log('更新'); }) } });

离线缓存(sw.js)


// self 当前线程中的this // 拦截用户发送的所有请求
let CACHE_NAME = `cache_version_` + 81;
let CACHAE_LIST = ["/", "/index.css", "/index.html", "main.js", "/getImage"];

// 独立的线程 可以使用fetch 但是不能使用ajax
function fetchAndSave(req) {
  return fetch(req).then(res => {
    // res 是流 // 做缓存操作
    let r = res.clone();
    caches.open(CACHE_NAME).then(cache => cache.put(req, r));
    return res;
  });
}

self.addEventListener("fetch", e => {
  // 用相应来替换 如果获取不到才用缓存
  let url = new URL(e.request.url);
  if (url.origin !== self.origin) {
    return;
  }

 // express 生成的一个接口
  if (e.request.url.includes("/getImage")) {
    // 调用了接口 // 如果遇到了接口 更新缓存
    e.respondWith(
      fetchAndSave(e.request).catch(err => {
        // 如果没网 在缓存中 匹配结果 返回请求
        return caches.match(e.request);
      })
    );
    return;
  }

  e.respondWith(
    fetch(e.request).catch(err => {
      // 如果没网 在缓存中 匹配结果 返回请求
      return caches.match(e.request);
    })
  );
});

// 用缓存替换 // serviceWorker安装的阶段
function preCache() {
  return caches.open(CACHE_NAME).then(cache => {
    return cache.addAll(CACHAE_LIST);
  });
}

self.addEventListener("install", e => {
  // 安装的过程中需要缓存
  e.waitUntil(preCache().then(skipWaiting));
});

function clearCache() {
  return caches.keys().then(keys => {
    return Promise.all(
      keys.map(key => {
        if (key !== CACHE_NAME) {
          return caches.delete(key);
        }
      })
    );
  });
}

self.addEventListener("activate", e => {
  e.waitUntil(
    Promise.all([
      clearCache(),
      self.clients.claim(), // 立即使serviceWorker生效
    ])
  );
});

写在最后:这个pwa 还是兼容性还是很弱,可以点击查看,对于一些需要用户留存度较高的应用的可以试试