service worker在新闻红包活动中的应用

蚊子前端博客
发布于 2019-02-26 16:44
活动页面大多是单页面应用,加载js后,再通过js进行页面的渲染,因此js的加载速度直接影响了页面呈现的速度。这里是想通过service worker缓存静态资源,期望能直接从缓存中加载静态资源,加载页面的呈现速度

首先声明下我们组的技术负担:前端没有自己的服务器,是依托于新闻CMS发布系统的。在这种情况下如何将service worker应用在新闻红包活动中呢?

1. 技术支持程度

活动页面大多是单页面应用,加载js后,再通过js进行页面的渲染,因此js的加载速度直接影响了页面呈现的速度。这里是想通过service worker缓存静态资源,期望能直接从缓存中加载静态资源,加载页面的呈现速度!

news.qq.com域名全面支持https后,service worker就可以提上日程了。首先进行技术支持程度的检测,使用'serviceWorker' in navigator检测功能的支持。在iOS12.0中,微信、QQ和新闻客户端,均不支持service worker,safari支持;在Android中,微信、QQ和新闻客户端都是支持的。

因此我们着重于Android下service worker的使用。同时,Android测试版新闻客户端,可是使用USB进行调试,调试service worker的工作状态。

2. 活动中的使用

2.1 尝试缓存相对路径的静态资源

刚开始在缓存资源时,因为知识储备的不足,钻了牛角尖,只想着缓存相对路径的资源,毕竟cms静态服务也是支持静态资源的相对路径的,可是在实验时发现,无论字段iscdn是否为true,图片类型的资源都会自动上传到CDN,那么就会造成js/css的地址和图片资源的地址不一致,毕竟baseUrl只能指定一个值,要么是js/css地址,要么是图片资源的地址。

刚开始想的是改造脚手架,用两个值分别来表示js/css地址和图片资源地址,但是脚手架的改造成本也是很高的!

2.2 缓存CDN中的资源

代码架构中的资源都上传到CDN,那么js/css/img都会在mat1.gtimg.com的CDN目录下,如果要加载并缓存CDN中的资源,CDN资源服务器的response中Access-Control-Allow-Origin中包含当前的页面所在域或者为*;幸运的是,mat1.gtimg.com服务器是支持的:

response:

COPYHTML

access-control-allow-origin: https://news.qq.com access-control-expose-headers: X-Client-Ip,X-Server-Ip,X-Upstream-Ip cache-control: max-age=60 content-encoding: gzip content-length: 50356 content-type: application/javascript; charset=utf-8 date: Tue, 15 Jan 2019 09:02:29 GMT expires: Tue, 15 Jan 2019 09:03:29 GMT last-modified: Tue, 15 Jan 2019 02:53:58 GMT server: NWSs status: 200 vary: Origin

因此我们可以在install中首先这个CDN下的静态资源进行离线缓存,然后在fetch中拦截请求,如果命中缓存则直接返回,如果没有命中,则去请求!在请求可控的跨域资源时,需要在请求头中加入{mode: 'cors'}

在install中,对首页和首页中一定会用到的静态资源进行缓存:

COPYJAVASCRIPT

const version = 'welfare-1.0.0'; const fileList = [ '/a/b/index.htm', 'https://mat1.gtimg.com/news/chunk-vendors.f9bab06e.js', 'https://mat1.gtimg.com/news/app.2677f99a.css', 'https://mat1.gtimg.com/news/app.af990eeb.js' ]; this.addEventListener('install', event => { this.skipWaiting(); event.waitUntil( caches.open(version).then(cache => { return cache.addAll(fileList); }) ); });

然后在fetch中拦截:

COPYJAVASCRIPT

// 检测当前请求是否应当被缓存 const checkHttpCanCache = (request, response) => { // 数据统计、请求的接口不缓存 // 同时若请求失败或者请求的资源无法控制,也不缓存 if ( request.url.indexOf('btrace.qq.com/kvcollect') > -1 || !response || response.status !== 200 || (response.type !== 'basic' && response.type !== 'cors') ) { return false; } return true; }; this.addEventListener('fetch', event => { let url = event.request.url; let req; if (url.indexOf('mat1.gtimg.com') > -1) { req = new Request(url, { mode: 'cors' }); } else { req = event.request.clone(); } event.respondWith( caches.match(event.request).then(response => { // 缓存中存在则直接返回 if (response) { return response; } // 否则进行请求并返回,这里还可以再多一步,如果可以缓存的资源,也进行缓存 let request = req.clone(); return fetch(request).then(httpRes => { if (!checkHttpCanCache(request, httpRes)) { return httpRes; } // 请求成功的话,将请求缓存起来 let responseClone = httpRes.clone(); caches.open(version).then(cache => { cache.put(request, responseClone); }); return httpRes; }); }); ); })

不过上面的fetch事件还不完整,如果页面中有的跨域资源的Access-Control-Allow-Origin不支持时,请求资源就会报错,会被浏览器阻止。比如展示列表中的图片,产品经理那边是把图片上传到img1.gtimg.com域名上,但是这个域名的Access-Control-Allow-Origin不支持。

关于这个域名下的请求,是这样处理的,需要在mode中加上no-cors

COPYJAVASCRIPT

if (url.indexOf('mat1.gtimg.com') > -1) { req = new Request(url, { mode: 'cors' }); } else if (url.indexOf('img1.gtimg.com') > -1) { req = new Request(url, { mode: 'no-cors' }); } else { req = event.request.clone(); }

2.3 验证service woker是否起作用

有时候会遇到这种情况,Cache Storage中明明已经有缓存了,但是每次刷新时,缓存不起作用,还是每次都走网络请求。针对这种情况,应当检查下当前页面是否是https链接,同时检查sw.js所在的路径是否跟当前页面地址在同一个目录中,比如页面路径为https://news.qq.com/a/b/index.htm,那么sw.js的路径也最好是/a/b/sw.js

service worker是否已生效,可以在chrome的network面板中查看,强刷缓存后,如果Size标识为(from ServiceWorker),则说明sw已生效;将网络设置为offline时,页面也依然能访问,service worker中缓存的数据能把首页的大致框架撑起来,只是没有接口数据了而已!

network中的资源请求-蚊子的前端博客

3. 总结

service worker能做的工作除了缓存资源,还有更多的功能可以使用,比如使用service worker模拟接口数据,在接口没有开发完成时,可以利用这个进行正常的对接接口开发等。

标签:
阅读(515) 评论(0)

公众号:

qrcode

微信公众号:前端小茶馆