React工程化之PWA之serviceWorker

我们使用create-react-app脚手架时,会发现入口文件index.js中有这样一个引入使用。

 

这个文件可以视情况用或者不用,它是用来做离线缓存等任务的,实际上就是为react项目注册了一个service worker。这样的话,如果在线上,只要访问过一次该网站,以后即使没有网络也可以访问(此时使用的是之前缓存的资源)。只在生产环境中有效(process.env.NODE_ENV === ‘production’)

在项目的public目录下,会有一个manifest.json文件,可以在这里做一些配置(图标、名字等等)。当用户把网页生成一个快捷方式时。会感觉用起来像原生APP一样。

源码分析

接下来我们来分析一下源码:看看其实现原理,代码很少也就100多行。该文件主要导出两个方法:

register():注册

register方法是默认(default)的导出方法。

export default function register() {
  //当前环境是生产环境并且浏览器支持serverWorker
  // The URL constructor is available in all browsers that support SW.
  if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
    //返回一个新的URL,作为生成静态文件夹的路径,下面说明...
    const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
    //如果静态文件和当前环境不在同一个域,注册没有意义,那就直接返回。
    if (publicUrl.origin !== window.location.origin) {
      return;
    }
   
    window.addEventListener('load', () => {
      //页面加载完成执行以下代码....
      const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
      if (isLocalhost) {
        // 如果是本地环境,调用checkValidServiceWorker进行注册
        checkValidServiceWorker(swUrl);
        // 注册成功后,打印信息
        navigator.serviceWorker.ready.then(() => {
          console.log(
            'This web app is being served cache-first by a service ' +
              'worker. To learn more, visit https://goo.gl/SC7cgQ'
          );
        });
      } else {
        // 如果不是本地环境(已经暴露在外网)仅仅只注册 service worker
        registerValidSW(swUrl);
      }
    });
  }
}

new URL(process.env.PUBLIC_URL, window.location),如果第一个参数是绝对路径,第二个参数忽略。如果第一个参数是相对路径,就会把第一个参数拼接到第二个参数后面。这里第一个参数是public目录的绝对路径。

unregister():取消注册
export function unregister() {
  //如果支持serviceWorker,并且处于就绪状态,那么调用其提供的取消注册方法
  if ('serviceWorker' in navigator) {
    navigator.serviceWorker.ready.then(registration => {
      registration.unregister();
    });
  }
}

补充:

一个文件中可以用多个export方法,但是只能有一个export default 方法。在使用import导入的时候,可以为export default导出的方法随意起名字且不用使用{}。普通导出的方法则反之,只能用{}并且使用导出时的名字。(可以这样说,用{}就必须使用导出时的名字)

如果按以下写法也是可以的,因为可以随意取名字。

import haha from './registerServiceWorker';
haha();

如果想使用普通导出方法,就需要加上{}了,而且名字必须和导出时的名字一样。

import {unregister} from './registerServiceWorker';
unregister();

然后就是一些内部方法了,在上面的方法中有调用:

isLocalhost:判断是不是本地环境

这其实是一个布尔值,通过匹配当前地址段判断当前是不是本地环境。

const isLocalhost = Boolean(
  window.location.hostname === 'localhost' ||
    // [::1] is the IPv6 localhost address.
    window.location.hostname === '[::1]' ||
    // 127.0.0.1/8 is considered localhost for IPv4.
    window.location.hostname.match(
      /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
    )
);
registerValidSW:注册有效serviceWorker
function registerValidSW(swUrl) {
  //注册有效的serviceWorker,然后使用提供的API进行操作
  navigator.serviceWorker
    .register(swUrl)
    .then(registration => {
      //如果内容有更新,就会自动进行安装
      registration.onupdatefound = () => {
        const installingWorker = registration.installing;
        installingWorker.onstatechange = () => {
          //安装之后判断安装状态进行提示
          if (installingWorker.state === 'installed') {
            if (navigator.serviceWorker.controller) {
              console.log('New content is available; please refresh.');
            } else {
              console.log('Content is cached for offline use.');
            }
          }
        };
      };
    })
    .catch(error => {
      console.error('Error during service worker registration:', error);
    });
}
checkValidServiceWorker:检测serviceWorker状态
function checkValidServiceWorker(swUrl) {
  // 向服务端申请资源
  fetch(swUrl)
    .then(response => {
      // 如果连接失败或者没有返回js
      if (
        response.status === 404 ||
        response.headers.get('content-type').indexOf('javascript') === -1
      ) {
        // 那么当 service worker 状态就绪的时候取消其注册状态
        navigator.serviceWorker.ready.then(registration => {
          registration.unregister().then(() => {
            //并且重新加载页面
            window.location.reload();
          });
        });
      } else {
        // 如果申请到资源,那么就调用 registerValidSW 方法来进行加载
        registerValidSW(swUrl);
      }
    })
    .catch(() => {
      console.log(
        'No internet connection found. App is running in offline mode.'
      );
    });
}


 

 

 

 

.

posted @ 2020-03-09 22:26  剑仙6  阅读(2408)  评论(0编辑  收藏  举报
欢迎访问个人网站www.qingchun.在线