阿里云「飞天发布时刻」2024来啦!新产品、新特性、新能力、新方案,等你来探~ 了解详情
写点什么

service worker 实现离线缓存

  • 2019-09-25
  • 本文字数:4348 字

    阅读完需:约 14 分钟

service worker 实现离线缓存

近两年前端比较热的话题之一就是 PWA(Progressive Web APP)——渐进式网页;致力于实现与原生 APP 相似的交互体验。


最早听说 pwa,是 2017 年年底的时候,看到朋友圈里的前端大神分享相关文章、应用。今年大概 3 月份的时候,看到 ios 从 11.3 开始支持后,就开始认真了解相关实现。


了解后发现,这一技术涉及到的内容很多。总结下来,pwa 的实现其实主要依赖于以下三点:


1)manifest 实现手机主界面的 web app 图标、标题、开屏 icon 等;


2)service worker 实现离线缓存请求、更新缓存、删除缓存;iOS safari 在 11.3 系统已支持;


3)push/notification 实现消息推送及接收。


其中 service worker 是离线应用的关键,我们主要来说说 service worker(以下简称 SW)。

1service worker 是什么

在说 SW 之前,先来回顾下 js 的单线程问题。


众所周知,js 在浏览器中是单线程运行的;主要是为了准确操作 DOM。但单线程存在的问题是,UI 线程和 js 线程需要抢占资源,在 js 执行耗时逻辑时,容易造成页面假死,用户体验较差。


由此出现了异步操作,可不影响主界面的响应。如 ajax、promise 等。


后来 html5 开放的 web worker 可以在浏览器后台挂载新线程。它无法直接操作 DOM,无法访问 window、document、parent 对象。


SW 是 web worker 的一种,也是挂载在浏览器后台运行的线程。主要用于代理网页请求,可缓存请求结果;可实现离线缓存功能。也拥有单独的作用域范围和运行环境

1.1 SW 使用限制

SW 除了 work 线程的限制外,由于可拦截页面请求,为了保证页面安全,浏览器端对 sw 的使用限制也不少。


1)无法直接操作 DOM 对象,也无法访问 window、document、parent 对象。可以访问 location、navigator;


2)可代理的页面作用域限制。默认是 sw.js 所在文件目录及子目录的请求可代理,可在注册时手动设置作用域范围;


3)必须在 https 中使用,允许在开发调试的 localhost 使用。

1.2 SW 兼容性

目前移动端 chrome 及 ios safari 11.3 以上已支持,pc 端火狐、谷歌、欧朋等支持,总体来看移动端及 pc 端都可以尝试使用。



不过在 chrome 里调试是最方便的,可以直观看到当前 SW 的状态、使用页面:



在 cacheStorage 查看缓存空间的存储信息:


1.3 service worker 可以解决的问题

1)用户多次访问网站时加快访问速度;


2)离线缓存接口请求及文件,更新、清除缓存内容;


3)可以在客户端通过 indexedDB API 保存持久化信息。

2 生命周期

生命周期有 5 步:注册、安装成功(安装失败)、激活、运行、销毁。


事件:install、activate、message、fetch、push、async。


由于是离线缓存,所以在首次注册、二次访问、服务脚本更新等会有不同的生命周期。

2.1 首次注册流程

从主线程注册后,会下载服务工作线程的 js。


安装:执行过程即是 installing 过程,此时会触发 install 事件,并执行 installEvent 的 waitUtil 方法。执行完毕后,当前状态是 installed。


激活:立即进入 activating 状态;并触发 activate 事件,处理相关事件内容。执行完成后,变成 activated 状态。


销毁: 当安装失败或进程被关闭时。


2.2 二次访问

在上一次服务未销毁时,二次访问页面,直接停留在激活运行状态:


2.3 服务脚本更新

如上图,sw.js 中,每次访问都会被下载一次。并且至少每 24 小时会被下载一次。为的是避免错误代码一直被运行。


下载后,会比对是否更新,如果更新,就会重新注册安装 serivceworker,安装成功后会处于 waiting 状态。


当 clients 都被关闭后,再次打开,才会激活最新的代码。


当然,也有方法可以跳过等待,比方说在安装完成后,执行 installEvent.skipWaiting()



3SW 的实际使用

3.1 注册

在主线程 main.js 中调起注册方法 register,register 只会被执行一次。


 1// 主线程的main.js 2// 先判断浏览器是否支持 3if('serviceWorker' in navigator){ 4  navigator.serviceWorker 5 6    // scope是自定义sw的作用域范围为根目录,默认作用域为当前sw.js所在目录的页面 7    .register('./sw.js', {scope: '/'}) 8 9    // 注册成功后会返回reg对象,指代当前服务线程实例10    .then(reg => {11      console.log('注册成功')  12    })13    .catch(error => {14      console.log('注册失败')15    })16}else{17  console.log('当前浏览器不支持SW')18}
复制代码

3.2 安装

在服务线程中添加安装监听及对应需缓存的资源文件:


 1// 在sw.js中监听对应的安装事件,并预处理需要缓存的文件 2// 该部分内容涉及到cacheStorage API 3 4// 定义缓存空间名称 5const CACHE_NAME = 'sylvia_cache' 6 7// 定义需要缓存的文件目录 8let filesToCache = [ 9  '/src/css/test.css',10  '/src/js/test.js'11]1213// 监听安装事件,返回installEvent对象14self.addEventListener('install', function(event){1516  // waitUntil方法执行缓存方法17  event.waitUntil(1819    // cacheStorage API 可直接用caches来替代20    // open方法创建/打开缓存空间,并会返回promise实例21    // then来接收返回的cache对象索引22    caches.open(CACHE_NAME).then(cache => {2324      // cache对象addAll方法解析(同fetch)并缓存所有的文件25      cache.addAll(filesToCache)26    })27  )28})
复制代码

3.3 激活

第一次注册并安装成功后,会触发 activate 事件:


1self.addEventListener('activate', event => {2  console.log('安装成功,即将监听作用域下的所有页面')3})
复制代码


在有 sw 脚本更新时,在后台默默注册安装新的脚本文件,安装成功后进入 waiting 状态。当前所有老版本控制的页面关闭后,再次打开时,新版本的脚本触发 activate 事件,此时可清除旧缓存,当前是修改 CACHE_NAME 的值来实现清除之前的缓存。


 1// 监听激活事件 2self.addEventListener('activate', event => { 3  // 获取所有的缓存key值,将需要被缓存的路径加载放到缓存空间下 4  var cacheDeletePromise = caches.keys().then(keyList => { 5    Promise.all(keyList.map(key => { 6      if(key !== CACHE_NAME){ 7        var deletePromise = caches.delete(key) 8        return deletePromise 9      }else{10        Promise.resolve()11      }12    }))13  })14  // 等待所有的缓存都被清除后,直接启动新的缓存机制15  event.waitUtil(16    Promise.all([cacheDeletePromise]).then(res => {17      this.clients.claim()18    })19  )20})
复制代码

3.4 运行

安装并激活成功后,就可以对页面实行 fetch 监控啦。


 1// 可以过滤使用已缓存的请求,若无缓存,由fetch发起新的请求,并返回给页面 2self.addEventListener('fetch', event => { 3  event.respondWith( 4    caches.match(event.request).then(res => { 5      if(res){ 6        return res 7      }else{ 8        return fetch(event.request) 9      }10    })11  )12})1314// 也可连续将请求缓存到内存里15self.addEventListener('fetch', event => {16  event.respondWith(17    caches.match(event.request).then(response => {18      if(response){19        return response20      }21      let requestClone = event.request.clone()22      return fetch(requestClone).then(res => {23        if(!res || res.status !== 200){24          return res25        }26        let resClone = res.clone()27        caches.open(CACHE_NAME).then(cache => {28          cache.put(event.request, resClone)29        })30        return res31      })32    })33  )34})
复制代码

3.5 进程销毁

1)当安装失败时会被浏览器丢弃该工作线程


2)浏览器后台启动 SW 时,会消耗性能,所以在不需要使用缓存时,可销毁


self.terminate()

4 应用框架 workbox

目前 chrome 有出一套完整的 SW 实用框架,可以较低成本的实现离线缓存


并提前封装好了对应所需的 API。

4.1 使用方法

在 sw.js 的文件中直接引入 workbox 的 cdn 上的文件:


1importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.0.0-alpha.3/workbox-sw.js')2if(workbox){3  console.log('your workbox is working now')4}else{5  console.log('it can't work!')6}7
复制代码


如果浏览器支持,可以直接引用 API 接口:


1)precaching,可以在注册成功后直接缓存的文件;


2)routing,匹配符合规则的 url,与 strategies 合作来完成文件的缓存。


示例:


 1// 注册完成后,即缓存对应的文件列表 2workbox.precaching.precacheAndRoute([ 3  '/src/static/js/index.js', 4  '/src/static/css/index/css', 5  {url: '/src/static/img/logo.png', revision" } 6]) 7 8// routing方法匹配请求文件路径,strategies用来存储对应文件 9workbox.routing.registerRoute(10  matchFunction,  // 字符串或者是正则表达式11  handler // 可以使用workbox.strategies缓存策略来缓存12)
复制代码


workbox.strategies 缓存策略有:


1)staleWhileRevalidate 使用已有的缓存,然后发起请求,用请求结果来更新缓存;


2)networkFirst 先发起请求,请求成功后会缓存结果。如果失败,则使用最新的缓存;


3)cacheFirst 总是先使用缓存,如果无匹配的缓存,则发起网络请求并缓存结果;


4)networkOnly 强制发起请求;


5)cacheOnly 强制使用缓存。


示例:


1 // 缓存使用方法2workbox.routing.registerRoute(3  '/src/index.js',4  workbox.strategies.staleWhileRevalidate()5)
复制代码

5 总结

以上可以实现简单的离线缓存。


使用场景


1)web 页面可将较少变动的静态资源放到客户端缓存中;


2)将一些基本数据缓存后,可以让用户在无网、弱网环境下,也能正常访问页面;


3)sw 其他的类似消息推送的功能,主要用在多页面同步消息,比如:邮件、即时通讯等。


但在实际使用中,一般离线内容有: 数据请求类、静态资源类、html 页面类等。


1)数据请求类:主要可缓存一些重要数据,需要注意数据量,一般浏览器分配给 sw 的空间是有限的,需要及时更新及删除无效数据;


2)静态资源类:一般是放在 cdn 上的文件,这时需要处理一些跨域缓存问题;


3)html 页面类:页面的缓存需要谨慎处理,因为如果注册 sw 的代码写在页面上的话,注意采用第二种缓存更新方法。


缓存更新,大多是两种:


1)引用新的 sw.js 的文件,即修改注册时的文件名;


2)修改 cacheStorage 中的缓存对象名称(推荐这种)。


回到 html 的缓存问题。如果缓存了注册 sw 的页面文件,那么访问时,总会从缓存中取页面内容。此时若采用第一种缓存方式,那么会出现 sw 永远无法更新的问题。


目前来看还是比较推荐使用 workbox 来接入,实现比较方便,缓存策略也比较实用。


作者介绍:


西薇亚(企业代号名),贝壳找房租赁平台前端工程师。


本文转载自公众号贝壳产品技术(ID:gh_9afeb423f390)。


原文链接:


https://mp.weixin.qq.com/s/aboA9dtCK6t0fzs0JU58Iw


2019-09-25 23:482761

评论

发布
暂无评论
发现更多内容

恒源云(GpuShare)_GPU租用保姆级教程,助力深度学习训练!

恒源云

面试官问的那些Java原理你都懂吗,Java面试手写代码题目

Java 程序员 后端

Serverless 工程实践 | Serverless 应用开发观念的转变

阿里巴巴云原生

Serverless Serverless架构

面试官手里那些秀你一脸的求质数大法,疯狂复习半个月

Java 程序员 后端

牛皮了!阿里大佬总结的图解Java手册在GitHub火了,完整版开源中

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

华为大神用前半生经验所写的SpringBoot全优笔记,现无偿与大家分享!

Java 华为 程序员 面试 计算机

面试官都被搞懵了,阿里P7亲自讲解

Java 程序员 后端

面试讲不清MySQL索引底层,Java面试

Java 程序员 后端

学生管理系统整理架构设计

小智

架构训练营

金九银十涨薪50%,从默默无闻,到坐上美团L8技术专家(面经+心得)

Java 编程 程序员 架构 面试

用遗传算法进行智能排课,相信老师会很喜欢

华为云开发者联盟

AI 编码 遗传算法 算子 课程编排

Alibaba船新制作“Java架构核心宝典”,全是流行技术,限时开放

Java 程序员 架构 面试 计算机

研发工具链介绍

百度开发者中心

学习 最佳实践 方法论 研发工具

一萌妹子的面试经历,美团四面三小时,成功拿到Java岗offer

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

面试竟然被这31道Java基础题难倒了,被阿里面试官征服了

Java 程序员 后端

KubeVirt with YRCloudFile 擦出创新的火花

焱融科技

云原生 文件存储 虚拟化 高性能, 分布式存储,

Apache APISIX 为 KubeSphere 提供更好用的网关及 K8S Ingress Controller

API7.ai 技术团队

Apache 开源 API网关 APISIX KubeSphere

模块3作业

Ping

ResNet-50 在 ImageNet-1k 上的实验笔记

毛显新

人工智能 神经网络 深度学习 卷积神经网络 PyTorch

小白都能看懂的JVM知识,一文带你学会JVM内存模型!

华为云开发者联盟

Java JVM 内存管理 Java虚拟机 JVM内存模型

【Vuex 源码学习】第四篇 - Vuex 中 Getters 的实现

Brave

源码 vuex 9月日更

阿里大佬怒写“Java初学者宝典”,让你就业没压力

Java 阿里巴巴 程序员 面试 计算机

南京主题展2021国际大数据产业展会/论坛会

南京专业智博会

大数据 智博会 南京智博会

Python中使用定时调度任务(Schedule Jobs)的5种方式

Regan Yue

Python 调度 9月日更

Alibaba内部最新Java架构核心宝典 (全彩版小册开源)

Java 程序员 架构 面试 计算机

SQL注入详解

行者AI

测试

webrtc simulcast 开启

webrtc developer

webrtc、 simulcast,

面试被问Tomcat整体架构设计,深入浅出Java开发

Java 程序员 后端

Vite + Vue3 + OpenLayers 手动控制缩放级别

德育处主任

大前端 地图 vite openlayers Vue 3

Python基础综合练习1

在即

9月日更

你的工作谁做主?

产品运营心经

工作效率 职场成长

service worker 实现离线缓存_文化 & 方法_西薇亚_InfoQ精选文章