美文网首页
浅析Service Worker

浅析Service Worker

作者: no_0405 | 来源:发表于2018-07-16 20:26 被阅读0次
    一、service worker是什么?

    平常浏览器窗口中跑的页面运行的是主JavaScript线程,DOM和window全局变量都是可以访问的。

    Service Worker是走的另外的线程,可以理解为在浏览器背后默默运行的一个线程,或者说是独立于当前页面的一段运行在浏览器后台进程里的脚本。

    它脱离浏览器窗体,异步地运行在一个完全独立的上下文环境,不会对主线程造成阻塞。在service worker中,window以及DOM都是不能访问的,但可以使用self访问全局上下文。

    二、service worker的作用?

    1.离线缓存(重点)
    2.消息推送(重点)
    3.后台数据同步
    4.响应来自其它源的资源请求,
    5.集中接收计算成本高的数据更新,比如地理位置和陀螺仪信息,这样多个页面就可以利6.用同一组数据
    7.在客户端进行CoffeeScript,LESS,CJS/AMD等模块编译和依赖管理(用于开发目的)
    8.后台服务钩子
    9.自定义模板用于特定URL模式
    10.性能增强,比如预取用户可能需要的资源,比如相册中的后面数张图片

    三、service worker的局限

    1、https: Service Worker必须是https协议的,但本地环境下http://localhost或者http://127.0.0.1也可以的。
    2、浏览器兼容性:

    image.png
    四、service worker的调试

    1、chrome://serviceworker-internals

    image.png
    2、网页中Application
    image.png image.png
    五、service worker的生命周期

    Service Worker生命周期的反应: installing → installed → activating → activated
    'install'用来缓存文件,'activate'用来缓存更新


    image.png
    六、service worker的用法

    1、html中

        if ('serviceWorker' in navigator) {
           // 开始注册service workers
           navigator.serviceWorker.register('./sw-demo-cache.js', {
               scope: './'
           }).then(function (registration) {
               console.log('注册成功');
               var serviceWorker;
               if (registration.installing) {
                   serviceWorker = registration.installing;
                   console.log('安装installing');
               } else if (registration.waiting) {
                   serviceWorker = registration.waiting;
                   console.log('等待waiting');
               } else if (registration.active) {
                   serviceWorker = registration.active;
                   console.log('激活active');
               }
               console.log('=>serviceWorker:', serviceWorker);
               if (serviceWorker) {
                   console.log(serviceWorker.state);
                   serviceWorker.addEventListener('statechange', function (e) {
                       console.log(' 状态变化为', e.target.state);
                   });
                    // 创建信道
                   var channel = new MessageChannel();
                   // port1留给自己
                   channel.port1.onmessage = e => {
                       console.log('main thread receive message...');
                       console.log(e);
                   }
                   console.log('给对方', window.RES_MAP);
                   // port2给对方
                   serviceWorker.postMessage(window.RES_MAP, [channel.port1]);
                   serviceWorker.addEventListener('statechange', function (e) {
                       // logState(e.target.state);
                   });
               }
           }).catch(function (error) {
               console.log('注册没有成功');
           });
       } else {
           console.log('不支持');
       }
    

    2、引进sw-demo-cache.js

    // sw
    self.addEventListener('message', ev => {
      console.log('sw receive message..');
      console.log(ev);
      fileMap = ev.data.RES_MAP;
      var arr1 = [].slice.call(fileMap); // ['a', 'b', 'c']
      // 取main thread传来的port2
      ev.ports[0].postMessage('Hi, hello too');
    });
    
    // var fs = require('fs');
    // console.log(fs);
    // 缓存
    self.addEventListener('install', function(event) {
      event.waitUntil(
        caches.open(VERSION).then(function(cache) {
          return cache.addAll([
            './index.html',
          ]);
        })
      );
    });
    
    // 缓存更新
    self.addEventListener('activate', function(event) {
      console.log('two now ready to handle fetches!');
      event.waitUntil(
        caches.keys().then(function(cacheNames) {
          return Promise.all(
            cacheNames.map(function(cacheName) {
              console.log('cacheName:', cacheName);
              // 如果当前版本和缓存版本不一致
              if (cacheName !== VERSION) {
                return caches.delete(cacheName);
              }
            })
          );
        })
      );
    });
    
    // 捕获请求并返回缓存数据
    self.addEventListener('fetch', function (event) {
      try{
        event.respondWith(
            caches.match(event.request).then(function(res){
                if(res){
                    return res;
                }
                requestBackend(event);
            })
        )
      } catch {
        console.log(event);
      }
    });
    
    function requestBackend(event){
      var url = event.request.clone();
      return fetch(url).then(function(res){
          //if not a valid response send the error
          if(!res || res.status !== 200 || res.type !== 'basic'){
              return res;
          }
          var response = res.clone();
          console.log('VERSION:', VERSION);
          caches.open(VERSION).then(function(cache){
              cache.put(event.request, response);
          });
    
          return res;
      })
    }
    

    3、webapck中获取文件目录
    引入第三个模块glob,递归获取打包后的文件目录

    exports.resMap = function () {
        var entryFiles = glob.sync(PAGE_PATH + '/*/*.js')
        var map = {}
        entryFiles.forEach((filePath) => {
            var filename = filePath.substring(filePath.lastIndexOf('\/') + 1, filePath.lastIndexOf('.'))
            map[filename] = filePath;
        })
        var entryFiles2 = glob.sync(PAGE_PATH2 + '/*')
        var map2 = {}
        findPath(entryFiles2, map2);
        console.log('map2', map2);
        return map2;
    };
    
    
    function findPath(entryFiles2, map2) {
        entryFiles2.forEach(filePath => {
            var filename = filePath.substring(filePath.lastIndexOf('/') + 1, filePath.lastIndexOf('.'));
            if (filePath.indexOf('.') <= 0) {
                let pathRes = path.resolve(__dirname, filePath);
                let files = glob.sync(pathRes + '/*');
                findPath(files, map2);
                map2[filename] = filePath;
            }
            map2[filename] = filePath;
        });
    }
    

    4、导出目录
    通过webpack的DefinePlugin插件,导出上步获取的目录
    5、web和service worker的通信
    通过postMessage实现web和service worker间的通信

                 // 创建信道
                    var channel = new MessageChannel();
                    // port1留给自己
                    channel.port1.onmessage = e => {
                        console.log('main thread receive message...');
                        console.log(e);
                    }
                    console.log('给对方', window.RES_MAP);
                    // port2给对方
                    serviceWorker.postMessage(window.RES_MAP, [channel.port1]);
                    serviceWorker.addEventListener('statechange', function (e) {
                        // logState(e.target.state);
                    });
    
    // sw
    self.addEventListener('message', ev => {
      console.log('sw receive message..');
      console.log(ev);
      fileMap = ev.data.RES_MAP;
      var arr1 = [].slice.call(fileMap); // ['a', 'b', 'c']
      // 取main thread传来的port2
      ev.ports[0].postMessage('Hi, hello too');
    });
    

    小结

    1、service worker让离线缓存成为可能,offline情况下也可以访问页面。然而销毁比较困难,更新会有问题。目前只能chrome://serviceworker-internals手动销毁,还待研究。

    参考:
    借助Service Worker和cacheStorage缓存及离线开发
    Service Worker 简介

    相关文章

      网友评论

          本文标题:浅析Service Worker

          本文链接:https://www.haomeiwen.com/subject/yzqipftx.html