前言:
我们的应用可以分为两部分,一部分是属于主进程的(包括js(同步,异步),以及dom渲染等等),在一个时刻点,只能执行一个,要么先去渲染dom,完了再去执行js;要么执行完js,在去渲染dom,而不能同时执行js和dom渲染。 另一部分属于worker进程,它重新在后台起了一个进程,它和应用的主进程互不影响,可以同时执行。
常见的worker有,web worker, service worker, shared worker等等。
其中service worker一般作为web应用程序、浏览器和网络(如果可用)之间的代理服务。他们旨在创建有效的离线体验,拦截网络请求,以及根据网络是否可用采取合适的行动,更新驻留在服务器上的资源。他们还将允许访问推送通知和后台同步API。用来构建PWA 应用
使用service-worker前,我们必须要先在主进程中注册它,然后才能在service-worker进程中编写逻辑。
主进程
//index.jsif ("serviceWorker" in navigator) { // Use the window load event to keep the page load performant window.addEventListener("load", () => { navigator.serviceWorker.register("/service-worker.js").then(registration=>{ console.log("register succces...") }, err=>{ console.log("register error...",err) }); }); }
service-worker进程
//service-worker.js console.log('Hello from service-worker.js');
service-worker的语法简介在service-worker.js中,self/this 表示 ServiceWorkerGlobalScope, 即全局的serviceworker工作环境,相当于在主进程中的window。在此文件中,js的其他api无法使用,如DOM,BOM操作等,但是大部分的js api可用,同时ES6也可以使用。
在service-worker中可以定义监听事件,然后在对应事件中进行逻辑处理。
具体api可查看 service worker MDN
service-worker进程的执行流程
首先在主进程中开始注册,调用register方法,进入sw进程,在sw进程中判断如果还没有安装service worker.js,则触发install事件。开始安装
一旦sw进程安装完成,会通知主进程register成功。
接着在sw进程 触发到activate事件。
如果已经安装过service-worker.js文件,则在注册时会发现并跳过install事件,直接进入注册成功,然后触发activate事件。
然后开始在sw进程中通过fetch事件,来监听http请求,并对请求和响应进行缓存。
//在service worker中监听install this.addEventListener('install', function(event) { event.waitUntil( caches.open('v1').then(function(cache) { return cache.addAll([ '/sw-test/', '/sw-test/index.html', '/sw-test/style.css', '/sw-test/app.js', '/sw-test/image-list.js', '/sw-test/star-wars-logo.jpg', '/sw-test/gallery/', '/sw-test/gallery/bountyHunters.jpg', '/sw-test/gallery/myLittleVader.jpg', '/sw-test/gallery/snowTroopers.jpg' ]); }) ); });
除了 install之外,还有 activate,message,fetch,sync,push等事件。
打开chrome浏览器的application->service workers,会看到
可以看到status为 actived and is running,表明service-worker已经安装成功了。
在service-worker中通过监听事件,然后编写对应的逻辑并不是一件容易的事,尤其对于文件缓存,可能npm run build后,名称随时会变。
所以chrome官方推出了wokbox框架
wokbox 是用于向web应用程序添加离线支持的JavaScript库。
要使用wokbox,只需在service-worker.js文件中引入workbox-sw.js即可,然后会自动的在service-worker.js中创建workbox对象,
importScripts(' if (workbox) { console.log(`Yay! Workbox is loaded 🎉`); } else { console.log(`Boo! Workbox didn't load 😬`); }
在wokbox对象中,包含很多模块,比如 workbox.routing模块,workbox.precaching模块,workbox.strategies模块,workbox.expiration模块等等,它们分别负责处理不同的逻辑。
//缓存文件 workbox.routing.registerRoute( /\.css$/, //通过正则匹配需要缓存哪些文件 new workbox.strategies.StaleWhileRevalidate({ cacheName: 'css-cache', //缓存名,可在application-> Cache storage下找到 }) ); workbox.routing.registerRoute( /\.(?:js)$/, new workbox.strategies.CacheFirst({ cacheName: 'js-cache', plugins: [ //设置过期时间和最大数量 new workbox.expiration.Plugin({ maxEntries: 20, maxAgeSeconds: 7 * 24 * 60 * 60, }) ], }) );
workbox.routing.registerRoute表明 当service-worker在安装之后,当页面有发送对应http请求时,开始缓存。
而下面的workbox.precaching.precacheAndRoute可以在service-worker在安装之前,就把对应文件预先缓存下来。
workbox.precaching.precacheAndRoute([ "/app.0.css", "/app.bundle.js", { url: "/start.html", revision: "dd75b1ef1ac2d4726b03fe46e90423f1" } ]);
此时我们在chrome下的 application->cache storage,会看到
缓存的名称,和缓存的文件列表
首先了解下处理路由的workbox的策略
StaleWhileRevalidate, 此策略将对请求使用缓存响应,并在后台使用网络响应更新缓存。如果没有缓存,它将等待网络响应并使用它),这是一种相当安全的策略,因为这意味着用户会定期更新其缓存。
NetworkFirst,这将首先尝试从网络获取请求。如果收到响应,它会将其传递给浏览器并将其保存到缓存中。如果网络请求失败,将使用最后一个缓存的响应。
CacheFirst,此策略将首先检查缓存中的响应,如果有可用则使用该策略。如果请求不在缓存中,则将使用网络,并且在传递给浏览器之前,任何有效响应都将添加到缓存中。
NetworkOnly,强制从网络获取。
CacheOnly,,强制从缓存获取。
workbox.routing.registerRoute( '/logo.png', //匹配字符串路由 new workbox.strategies.NetworkFirst() //采用NetworkFirst策略);
workbox.routing.registerRoute( /\.js$/, // 配置 正则 路由, new workbox.strategies.StaleWhileRevalidate(), //采用StaleWhileRevalidate策略);
//缓存第三方,比如jquery, 则策略最好使用NetworkFirst或者StaleWhileRevalidate, 不要使用CacheFirstworkbox.routing.registerRoute( 'https://cdn.bootcss.com/jquery/3.4.1/jquery.js', new workbox.strategies.StaleWhileRevalidate(), //new workbox.strategies.NetworkFirst(),); //如果非要使用CacheFirst策略,则使用workbox.cacheableResponse.Plugin限定workbox.routing.registerRoute( 'https://cdn.bootcss.com/jquery/3.4.1/jquery.js', new workbox.strategies.CacheFirst({ plugins: [ new workbox.cacheableResponse.Plugin({ statuses: [0, 200] }) ] }), );
//还可以自定义策略的名称,过期时间等等workbox.routing.registerRoute( /\.(?:js)$/, new workbox.strategies.CacheFirst({ cacheName: 'js-cache', plugins: [ new workbox.expiration.Plugin({ maxEntries: 20, maxAgeSeconds: 7 * 24 * 60 * 60, }) ], }) );
在前面我们看到 cache的名称为 workbox-precache-v2-http://127.0.0.1:8081/,下面我们修改下
//最好写在紧贴着importScripts workbox-sw.js的下面,如果写在文件最后,则不生效。workbox.core.setCacheNameDetails({ prefix: "my-app", suffix: "v1", precache: "custom-precache-name", runtime: "custom-runtime-name"});
workbox.backgroundSync.Plugin:如果网络请求失败,请将其添加到后台同步队列,并在触发下一个同步事件时重试该请求。
workbox.broadcastUpdate.Plugin:每当缓存更新调度时,广播频道上的消息或通过 postMessage()。
workbox.cacheableResponse.Plugin:仅缓存符合特定条件的缓存请求。
workbox.expiration.Plugin:管理缓存中项目的数量和最长期限。
workbox.rangeRequests.Plugin:响应包含Range:标头的请求,其中包含来自缓存的部分内容。
importScripts(" importScripts("precache-manifest.7df9e91fe595ae52486747ebe221a710.js"); //强制在service worker中使用debug。这样service worker中的log也能打印到chrome console上 workbox.setConfig({ debug: true });
yarn add workbox-webpack-plugin
//webpack.config.js:const WorkboxPlugin = require('workbox-webpack-plugin');module.exports = { // Other webpack config... plugins: [ // Other plugins... new WorkboxPlugin.GenerateSW() ] };
然后执行
npm run build
此时在dist目录下会自动生成precache-manifest.<revision>.js 和 service-worker.js文件(为什么名字是这个,不是sw.js,因为在注册时register("/service-worker.js")写的这个名字),如图:
在precache-manifest.<revision>.js文件中,将预缓存列表通过全局变量self.__precacheManifest公开,以便在service-worker.js中调用。
默认会预缓存一切资源。
在service-worker.js中,则自动加载workbox cdn和 precache-manifest.<revision>.js文件,如图:
瞬间感觉方便了很多........
因为默认会预缓存一切资源,如果你不喜欢预缓存某些文件,如图片,而在运行时缓存,则可以在runtimeCaching中定制它们
// 这些选项帮助 ServiceWorkers 快速启用 new WorkboxPlugin.GenerateSW({ // 在预缓存中排除 图片 exclude: [/\.(?:png|jpg|jpeg|svg)$/], //定义运行时缓存(可接受多个json对象) runtimeCaching: [ { urlPattern: /\.(?:png|jpg|jpeg|svg)$/, // 在缓存时使用 StaleWhileRevalidate 策略. handler: "StaleWhileRevalidate", options: { // 定义缓存这些图片的 cache名称 cacheName: "my-images-cache", //配置 expiration expiration: { maxEntries: 10, maxAgeSeconds: 60 }, // 配置 background sync. backgroundSync: { name: "my-queue-name", options: { maxRetentionTime: 60 * 60 } }, //配置哪些响应被认为是可缓存的 cacheableResponse: { statuses: [0, 200], headers: { "x-test": "true" } }, //配置广播缓存更新插件。 broadcastUpdate: { channelName: "my-update-channel" }, //matchOptions和fetchOptions用于配置handler fetchOptions: { mode: "no-cors" }, matchOptions: { ignoreSearch: true } } } ], importWorkboxFrom: "cdn", //通过cdn加载workbox, 还可通过‘local’加载,这样会将整个workbox下载到本地,再从本地引用 skipWaiting: false, // service worker是否应该跳过等待生命周期阶段 clientsClaim: false, //service worker是否应该在任何现有客户端激活后立即开始控制它 cacheId: "my-app-test", offlineGoogleAnalytics: true })
//最好写在紧贴着importScripts workbox-sw.js的下面,如果写在文件最后,则不生效。 workbox.core.setCacheNameDetails({ prefix: "my-app", suffix: "v1", precache: "custom-precache-name", runtime: "custom-runtime-name" });
如果没出现,则只需要清空cache和service-worker文件即可,在clear storage中勾选unregister service workers和 cache storage,然后点击clear site data即可清理,然后刷新页面就会看到最新的service worker和cache storage。
我们会发现不管vue还是react,构建后,点开service-worker.js文件发现都和我们刚才自动生成的service-worker.js非常相似,不同的是,vue和react都把new WorkboxPlugin.GenerateSW部分封装了起来,除非npm run eject才能看到和修改。
CMS(Content Management System)是网站内容管理系统简称, 互联网上每个网站(无论大小门户)其后台都由专业CMS系统支撑- Zoomla!逐浪CMS作为国内高端CMS与WEB应用典范,首创第3代CMS理念,专注底层核心技术研发,以云技术、创新精神构建行业新成就,提供从网站内核到电商、办公、移动一体化的开发体验!