service后端开发(使用ServiceWorker让你的Web)

service后端开发(使用ServiceWorker让你的Web)(1)

作者:网易@brizer

转发链接:https://github.com/omnipotent-front-end/blog/issues/2

目录

使用Service Worker让你的 Web 应用如虎添翼(上)「干货」

使用Service Worker让你的 Web 应用如虎添翼(中)「干货」

runtimecache

运行时缓存是在install之后,activated和fetch阶段做的事情。

既然在fetch阶段发送,那么runtimecache 往往应对着各种类型的资源,对于不同类型的资源往往也有不同的缓存策略。

缓存策略

workbox提供的缓存策划有以下几种,通过不同的配置可以针对自己的业务达到不同的效果:

staleWhileRevalidate

这种策略的意思是当请求的路由有对应的 Cache 缓存结果就直接返回,再返回 Cache 缓存结果的同时会在后台发起网络请求拿到请求结果并更新 Cache 缓存,如果本来就没有 Cache 缓存的话,直接就发起网络请求并返回结果,这对用户来说是一种非常安全的策略,能保证用户最快速的拿到请求的结果。

但是也有一定的缺点,就是还是会有网络请求占用了用户的网络带宽。可以像如下的方式使用 State While Revalidate 策略:

workbox.routing.registerRoute( newRegExp('https://edu-cms\.nosdn\.127\.net/topics/'), workbox.strategies.staleWhileRevalidate({ //cache名称 cacheName:'lf-sw:static', plugins:[ newworkbox.expiration.Plugin({ //cache最大数量 maxEntries:30 }) ] }) );

networkFirst

这种策略就是当请求路由是被匹配的,就采用网络优先的策略,也就是优先尝试拿到网络请求的返回结果,如果拿到网络请求的结果,就将结果返回给客户端并且写入 Cache 缓存。

如果网络请求失败,那最后被缓存的 Cache 缓存结果就会被返回到客户端,这种策略一般适用于返回结果不太固定或对实时性有要求的请求,为网络请求失败进行兜底。可以像如下方式使用 Network First 策略:

//自定义要缓存的html列表 varcacheList=[ '/Hexo/public/demo/PWADemo/workbox/index.html' ]; workbox.routing.registerRoute( //自定义过滤方法 function(event){ //需要缓存的HTML路径列表 if(event.url.host==='localhost:63342'){ if(~cacheList.indexOf(event.url.pathname))returntrue; elsereturnfalse; }else{ returnfalse; } }, workbox.strategies.networkFirst({ cacheName:'lf-sw:html', plugins:[ newworkbox.expiration.Plugin({ maxEntries:10 }) ] }) );

cacheFirst

这个策略的意思就是当匹配到请求之后直接从 Cache 缓存中取得结果,如果 Cache 缓存中没有结果,那就会发起网络请求,拿到网络请求结果并将结果更新至 Cache 缓存,并将结果返回给客户端。这种策略比较适合结果不怎么变动且对实时性要求不高的请求。可以像如下方式使用 Cache First 策略:

workbox.routing.registerRoute( newRegExp('https://edu-image\.nosdn\.127\.net/'), workbox.strategies.cacheFirst({ cacheName:'lf-sw:img', plugins:[ //如果要拿到域外的资源,必须配置 //因为跨域使用fetch配置了 //mode:'no-cors',所以status返回值为0,故而需要兼容 newworkbox.cacheableResponse.Plugin({ statuses:[0,200] }), newworkbox.expiration.Plugin({ maxEntries:40, //缓存的时间 maxAgeSeconds:12*60*60 }) ] }) );

networkOnly

比较直接的策略,直接强制使用正常的网络请求,并将结果返回给客户端,这种策略比较适合对实时性要求非常高的请求。

cacheOnly

这个策略也比较直接,直接使用 Cache 缓存的结果,并将结果返回给客户端,这种策略比较适合一上线就不会变的静态资源请求。

举个例子

又到了举个例子的阶段了,这次我们用淘宝好了,看看他们是如何通过workbox来配置serviceworker的:

//首先是异常处理 self.addEventListener('error',function(e){ self.clients.matchAll() .then(function(clients){ if(clients&&clients.length){ clients[0].postMessage({ type:'ERROR', msg:e.message||null, stack:e.error?e.error.stack:null }); } }); }); self.addEventListener('unhandledrejection',function(e){ self.clients.matchAll() .then(function(clients){ if(clients&&clients.length){ clients[0].postMessage({ type:'REJECTION', msg:e.reason?e.reason.message:null, stack:e.reason?e.reason.stack:null }); } }); }) //然后引入workbox importScripts('https://g.alicdn.com/kg/workbox/3.3.0/workbox-sw.js'); workbox.setConfig({ debug:false, modulePathPrefix:'https://g.alicdn.com/kg/workbox/3.3.0/' }); //直接激活跳过等待阶段 workbox.skipWaiting(); workbox.clientsClaim(); //定义要缓存的html varcacheList=[ '/', '/tbhome/home-2017', '/tbhome/page/market-list' ]; //html采用networkFirst策略,支持离线也能大体访问 workbox.routing.registerRoute( function(event){ //需要缓存的HTML路径列表 if(event.url.host==='www.taobao.com'){ if(~cacheList.indexOf(event.url.pathname))returntrue; elsereturnfalse; }else{ returnfalse; } }, workbox.strategies.networkFirst({ cacheName:'tbh:html', plugins:[ newworkbox.expiration.Plugin({ maxEntries:10 }) ] }) ); //静态资源采用staleWhileRevalidate策略,安全可靠 workbox.routing.registerRoute( newRegExp('https://g\.alicdn\.com/'), workbox.strategies.staleWhileRevalidate({ cacheName:'tbh:static', plugins:[ newworkbox.expiration.Plugin({ maxEntries:20 }) ] }) ); //图片采用cacheFirst策略,提升速度 workbox.routing.registerRoute( newRegExp('https://img\.alicdn\.com/'), workbox.strategies.cacheFirst({ cacheName:'tbh:img', plugins:[ newworkbox.cacheableResponse.Plugin({ statuses:[0,200] }), newworkbox.expiration.Plugin({ maxEntries:20, maxAgeSeconds:12*60*60 }) ] }) ); workbox.routing.registerRoute( newRegExp('https://gtms01\.alicdn\.com/'), workbox.strategies.cacheFirst({ cacheName:'tbh:img', plugins:[ newworkbox.cacheableResponse.Plugin({ statuses:[0,200] }), newworkbox.expiration.Plugin({ maxEntries:30, maxAgeSeconds:12*60*60 }) ] }) );

可以看出,使用workbox比起直接手撸来,要快很多,也明确很多。

原理

目前分析serviceworker和workbox的文章不少,但是介绍workbox原理的文章却不多。这里简单介绍下workbox这个工具库的原理。

首先将几个我们产品用到的模块图奉上:

service后端开发(使用ServiceWorker让你的Web)(2)

简单提几个workbox源码的亮点。

通过Proxy按需依赖

熟悉了workbox后会得知,它是有很多个子模块的,各个子模块在通过用到的时候按需importScript到线程中。

service后端开发(使用ServiceWorker让你的Web)(3)

做到按需依赖的原理就是通过Proxy对全局对象workbox进行代理:

newProxy(this,{ get(t,s){ //如果workbox对象上不存在指定对象,就依赖注入该对象对应的脚本 if(t[s])returnt[s]; consto=e[s]; returno&&t.loadModule(`workbox-${o}`),t[s]; } })

如果找不到对应模块,则通过importScripts主动加载:

/** *加载前端模块 *@param{Strnig}t */ loadModule(t){ conste=this.o(t); try{ importScripts(e),(this.s=!0); }catch(s){ throw(console.error(`Unabletoimportmodule'${t}'from'${e}'.`),s); } }

通过freeze冻结对外暴露api

workbox.core模块中提供了几个核心操作模块,如封装了indexedDB操作的DBWrapper、对cacheStorage进行读取的cacheWrapper,以及发送请求的fetchWrapper和日志管理的logger等等。

为了防止外部对内部模块暴露出去的api进行修改,导致出现不可预估的错误,内部模块可以通过Object.freeze将api进行冻结保护:

var_private=/*#__PURE__*/Object.freeze({ DBWrapper:DBWrapper, WorkboxError:WorkboxError, assert:finalAssertExports, cacheNames:cacheNames, cacheWrapper:cacheWrapper, fetchWrapper:fetchWrapper, getFriendlyURL:getFriendlyURL, logger:defaultExport });

总结

通过对serviceworker的理解和workbox的应用,可以进一步提升产品的性能和弱网情况下的体验。有兴趣的同学也可以对workbox的源码细细评读,其中还有很多不错的设计模式和编程风格值得学习。

本篇已完结

推荐JavaScript经典实例学习资料文章

《使用Service Worker让你的 Web 应用如虎添翼(上)「干货」》

《使用Service Worker让你的 Web 应用如虎添翼(中)「干货」》

《前端如何一次性处理10万条数据「进阶篇」》

《推荐三款正则可视化工具「JS篇」》

《如何让用户选择是否离开当前页面?「JS篇」》

《JavaScript开发人员更喜欢Deno的五大原因》

《仅用18行JavaScript实现一个倒数计时器》

《图文细说JavaScript 的运行机制》

《一个轻量级 JavaScript 全文搜索库,轻松实现站内离线搜索》

《推荐Web程序员常用的15个源代码编辑器》

《10个实用的JS技巧「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(一)「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(二)「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(三)「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(四)「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(五)「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(六)「值得收藏」》

《深入JavaScript教你内存泄漏如何防范》

《手把手教你7个有趣的JavaScript 项目-上「附源码」》

《手把手教你7个有趣的JavaScript 项目-下「附源码」》

《JavaScript 使用 mediaDevices API 访问摄像头自拍》

《手把手教你前端代码如何做错误上报「JS篇」》

《一文让你彻底搞懂移动前端和Web 前端区别在哪里》

《63个JavaScript 正则大礼包「值得收藏」》

《提高你的 JavaScript 技能10 个问答题》

《JavaScript图表库的5个首选》

《一文彻底搞懂JavaScript 中Object.freeze与Object.seal的用法》

《可视化的 JS:动态图演示 - 事件循环 Event Loop的过程》

《教你如何用动态规划和贪心算法实现前端瀑布流布局「实践」》

《可视化的 js:动态图演示 Promises & Async/Await 的过程》

《原生JS封装拖动验证滑块你会吗?「实践」》

《如何实现高性能的在线 PDF 预览》

《细说使用字体库加密数据-仿58同城》

《Node.js要完了吗?》

《Pug 3.0.0正式发布,不再支持 Node.js 6/8》

《纯JS手写轮播图(代码逻辑清晰,通俗易懂)》

《JavaScript 20 年 中文版之创立标准》

《值得收藏的前端常用60余种工具方法「JS篇」》

《箭头函数和常规函数之间的 5 个区别》

《通过发布/订阅的设计模式搞懂 Node.js 核心模块 Events》

《「前端篇」不再为正则烦恼》

《「速围」Node.js V14.3.0 发布支持顶级 Await 和 REPL 增强功能》

《深入细品浏览器原理「流程图」》

《JavaScript 已进入第三个时代,未来将何去何从?》

《前端上传前预览文件 image、text、json、video、audio「实践」》

《深入细品 EventLoop 和浏览器渲染、帧动画、空闲回调的关系》

《推荐13个有用的JavaScript数组技巧「值得收藏」》

《前端必备基础知识:window.location 详解》

《不要再依赖CommonJS了》

《犀牛书作者:最该忘记的JavaScript特性》

《36个工作中常用的JavaScript函数片段「值得收藏」》

《Node H5 实现大文件分片上传、断点续传》

《一文了解文件上传全过程(1.8w字深度解析)「前端进阶必备」》

《【实践总结】关于小程序挣脱枷锁实现批量上传》

《手把手教你前端的各种文件上传攻略和大文件断点续传》

《字节跳动面试官:请你实现一个大文件上传和断点续传》

《谈谈前端关于文件上传下载那些事【实践】》

《手把手教你如何编写一个前端图片压缩、方向纠正、预览、上传插件》

《最全的 JavaScript 模块化方案和工具》

《「前端进阶」JS中的内存管理》

《JavaScript正则深入以及10个非常有意思的正则实战》

《前端面试者经常忽视的一道JavaScript 面试题》

《一行JS代码实现一个简单的模板字符串替换「实践」》

《JS代码是如何被压缩的「前端高级进阶」》

《前端开发规范:命名规范、html规范、css规范、js规范》

《【规范篇】前端团队代码规范最佳实践》

《100个原生JavaScript代码片段知识点详细汇总【实践】》

《关于前端174道 JavaScript知识点汇总(一)》

《关于前端174道 JavaScript知识点汇总(二)》

《关于前端174道 JavaScript知识点汇总(三)》

《几个非常有意思的javascript知识点总结【实践】》

《都2020年了,你还不会JavaScript 装饰器?》

《JavaScript实现图片合成下载》

《70个JavaScript知识点详细总结(上)【实践】》

《70个JavaScript知识点详细总结(下)【实践】》

《开源了一个 JavaScript 版敏感词过滤库》

《送你 43 道 JavaScript 面试题》

《3个很棒的小众JavaScript库,你值得拥有》

《手把手教你深入巩固JavaScript知识体系【思维导图】》

《推荐7个很棒的JavaScript产品步骤引导库》

《Echa哥教你彻底弄懂 JavaScript 执行机制》

《一个合格的中级前端工程师需要掌握的 28 个 JavaScript 技巧》

《深入解析高频项目中运用到的知识点汇总【JS篇】》

《JavaScript 工具函数大全【新】》

《从JavaScript中看设计模式(总结)》

《身份证号码的正则表达式及验证详解(JavaScript,Regex)》

《浏览器中实现JavaScript计时器的4种创新方式》

《Three.js 动效方案》

《手把手教你常用的59个JS类方法》

《127个常用的JS代码片段,每段代码花30秒就能看懂-【上】》

《深入浅出讲解 js 深拷贝 vs 浅拷贝》

《手把手教你JS开发H5游戏【消灭星星】》

《深入浅出讲解JS中this/apply/call/bind巧妙用法【实践】》

《手把手教你全方位解读JS中this真正含义【实践】》

《书到用时方恨少,一大波JS开发工具函数来了》

《干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React)》

《手把手教你JS 异步编程六种方案【实践】》

《让你减少加班的15条高效JS技巧知识点汇总【实践】》

《手把手教你JS开发H5游戏【黄金矿工】》

《手把手教你JS实现监控浏览器上下左右滚动》

《JS 经典实例知识点整理汇总【实践】》

《2.6万字JS干货分享,带你领略前端魅力【基础篇】》

《2.6万字JS干货分享,带你领略前端魅力【实践篇】》

《简单几步让你的 JS 写得更漂亮》

《恭喜你获得治疗JS this的详细药方》

《谈谈前端关于文件上传下载那些事【实践】》

《面试中教你绕过关于 JavaScript 作用域的 5 个坑》

《Jquery插件(常用的插件库)》

《【JS】如何防止重复发送ajax请求》

《JavaScript Canvas实现自定义画板》

《Continuation 在 JS 中的应用「前端篇」》

作者:网易@brizer

转发链接:https://github.com/omnipotent-front-end/blog/issues/2

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页