service worker 从入门到放弃

572 阅读3分钟

service worker如何用在vue 或者react 项目中使用

function registerSw(){
  if('serviceWorker' in window.navigator){
    navigator.serviceWorker.register('/ggtest.js').then(res=>{
      console.log('success')
    }).catch(err=>{
      console.log('fail')
    })
  }
}
module.exports= registerSw

然后在 项目入口文件引入,并调用方法

现在来说注意事项, navigator.serviceWorker.register('/service-worker.js') ,/service-worker.js 文件所在位置是指基于打包或者编译后的位置。不是当前的引用位置。但是因为目前vue和react项目基本上大部分都是基于webpack 打包,会将文件打包,然后生成hash值,导致引用异常。

解决方案:copy-webpack-plugin 插件 使用方式:

 // 不传 默认就是打包后的根目录 ,另外webpack5 在写法上有调整。
 
 new CopyWebpackPlugin([{ from: path.resolve(__dirname, '../static') }])
 //webpack5 
new CopyWebpackPlugin({patterns: [{ from: path.resolve(__dirname, '../static') }]}),

缓存静态文件和get请求的,网上一堆的资料,可以自己去看下。主要是讲一下如何缓存post请求

首先判断一下当前是不是个post请求

this.addEventListener('fetch',function(event){
    if(event.request.method =='POST'){
        ...
    }
})

首先明确一下 catchStorage 是不支持存储 post 请求的,并且service worker 不是运行在主线程的,是没有window对象的。所以只能使用indexdb去做存储。所以我们需要引入一个封装过的indexdb 库 dexie.js,(dexie.js文件地址也是打包后的地址,然后不是通过import 或者require 方法,不支持)

importScripts('/static/dexie.js')
this.addEventListener('fetch',function(event){
    if(event.request.method =='POST'){
    
        // dexie 使用
        var db = new Dexie('post_cache');
        db.version(1).stores({
          post_cache: 'key, response,token, timestamp'
          // post_cache 表名
          // key, response,token, timestamp  四个都是字段名
        })
        // 这样就开起了一个post_cache的indexdb库下的post_cache表
        ...
        
    }
})

首先来个常规写法,就是正常还是走接口请求,但是接口异常时才走缓存数据。

this.addEventListener('fetch',function(event){
  // console.log('fetch window',window)
 if(event.request.method =='POST'){
    var db = new Dexie('post_cache');
    db.version(1).stores({
      post_cache: 'key, response,token, timestamp'
    })
    
    event.respondWith(
     fetch(event.request.clone())
        .then(function(response) {
            // 后面讲
            cachePut(event.request.url, response.clone(),db.post_cache);
            return response;
        })
        .catch(function() {
            //后面讲
            return cacheMatch(event.request.url,db.post_cache)
        })
    )
  }
});

首先来讲一下,cachePut 函数吧,先描述一下吧,因为所有的service worker的操作都是异步操作,所以先要通过方法异步去获取数据,并且等数据转换好了之后再去添加到indexdb中保存,先上代码:

// 获取请求头里面的所有的参数
function serializeHeaders(headers) {
  var serialized = {};
  for (var entry of headers.entries()) {
  serialized[entry[0]] = entry[1];
  }
  return serialized;
}
// 
function serializeResponse(response) {
  var serialized = {
  headers: serializeHeaders(response.headers),
  status: response.status,
  statusText: response.statusText,
  ok:response.ok,
  bodyUsed:response.bodyUsed,
  type:response.type,
  url:response.url
  };
  // serialized 这个对象就是按照接口返回的数据结构构造的,但是没有 body 
  return Promise.resolve(serialized);
}
function cachePut(defaultUrl, response,token, store) {
  serializeResponse(response.clone()).then((resp)=>{
      
    ....
  }
}

现在来看下是如何获取body的吧

function cachePut(defaultUrl, response,token, store) {
  serializeResponse(response.clone()).then((resp)=>{
      // body 又是一层 异步 
    response.clone().text().then(res=>{
      let body = {...resp,body:res}
      // entry 就是我们需要存入indexdb的数据
      var entry ={
        key :defaultUrl,
        response:body,
        token,
        timestamp: Date.now()
      }
      
      store.add(entry).catch(function(error){
          // 当添加数据异常的时候尝试更新数据
        store.update(entry.key,entry)
      })
    })
  })

}

到这里数据存完了,但是总得用吧。(手动斜眼笑),这就来到了我们 cacheMatch 方法了

function deserializeResponse(data) {
    // 返回的数据一定要是一个异步数据,并且是一个Response对象。
  return Promise.resolve(new Response(data.body, data));
}
function cacheMatch(requestUrl,token,store) {
  return store.get(requestUrl).then(function(data){
      // 获取数据 
    if (data) {
      // 用户切换身份,token 这个主要是为了防止接口缓存了,但是用户身份切换了接口未更新导致《加班》
      if(data.token != token ){
        return undefined
      }    
      return deserializeResponse(data.response);
    } else {
        //未取到数据
      return undefined
    }
  });
}

到了这里,现在来介绍一下如何优先缓存其次是fetch了。

this.addEventListener('fetch',function(event){
  if(event.request.method =='POST'){
    var db = new Dexie('post_cache');
    db.version(1).stores({
      post_cache: 'key, response,token, timestamp'
    })
    
    event.respondWith(
      async function() {
        var token = ''
        const reqHead = event.request.clone().headers
        for (var entry of reqHead.entries()) {
          if(entry[0] =='j_token'){
            token = entry[1]
          }
        }
        const response = await cacheMatch(event.request.url,token,db.post_cache)
        return response || fetch(event.request.clone())
        .then(function(response) {
            cachePut(event.request.url, response.clone(), token,db.post_cache);
            return response;
        })
      }()
    )
  }
});