惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
H
Help Net Security
量子位
The Cloudflare Blog
博客园 - Franky
博客园 - 聂微东
博客园 - 司徒正美
Last Week in AI
Last Week in AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Apple Machine Learning Research
Apple Machine Learning Research
宝玉的分享
宝玉的分享
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
有赞技术团队
有赞技术团队
罗磊的独立博客
GbyAI
GbyAI
雷峰网
雷峰网
T
The Blog of Author Tim Ferriss
Martin Fowler
Martin Fowler
S
SegmentFault 最新的问题
美团技术团队
阮一峰的网络日志
阮一峰的网络日志
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
U
Unit 42
MongoDB | Blog
MongoDB | Blog

蚊子的前端博客

微说 | 有的人觉得只要不考XX,一定能考好!-蚊子的前端博客 微说 | 春天来了-蚊子的前端博客 明天和意外不知道哪个先来-蚊子的前端博客 微说 | 既然错过了路口,就及时止损,重新规划路线上路-蚊子的前端博客 微说 | 2025年的出生人口数是792万-蚊子的前端博客 微说 | 工作年终总结,让写对接的接口的数量?-蚊子的前端博客 微说 | 温家宝:没有政治体制改革的成功 经济体制改革不可能进行到底-蚊子的前端博客 微说 | 违法犯罪了该不该被禁言?-蚊子的前端博客 微说 | 不明白为什么在推荐去俄罗斯旅游-蚊子的前端博客 微说 | 易中天论骗子-蚊子的前端博客 微说 | 一场大风吹散了秋天-蚊子的前端博客 微说 | 既要、又要、还要、更要!-蚊子的前端博客 又是一年的国庆雨季-蚊子的前端博客 微说 | 预估下2025年的出生人口数据-蚊子的前端博客 微说 | 可惜了我那些小时候的书本-蚊子的前端博客 微说 | 牛马有的是,驴不够了!-蚊子的前端博客 微说 | 能否有一条非户口也能高考的路-蚊子的前端博客 微说 | 小聊中医-蚊子的前端博客 微说 | 将要制作的一款新产品-蚊子的前端博客 微说 | 做人要有信-蚊子的前端博客 微说 | 好一个正义联盟-蚊子的前端博客 微说 | 都是见过吃过的主儿-蚊子的前端博客 微说 | 追求8小时工作制有错吗?-蚊子的前端博客 微说 | 如果尖锐的批评完全消失-蚊子的前端博客 微说 | 很好!-蚊子的前端博客 微说 | 封禁用户可以,但要告知具体原因-蚊子的前端博客 微说 | 面朝大海,春暖花开-蚊子的前端博客 微说 | 程序员的悲哀是什么?-蚊子的前端博客 微说 | 2025年出生人口的预测-蚊子的前端博客 前端在 LiveKit 中如何获取所有的参与者-蚊子的前端博客
service worker在新闻红包活动中的应用-蚊子的前端博客
author · 2019-02-26 · via 蚊子的前端博客

活动页面大多是单页面应用,加载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:

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 中,对首页和首页中一定会用到的静态资源进行缓存:

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 中拦截:

// 检测当前请求是否应当被缓存
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

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 模拟接口数据,在接口没有开发完成时,可以利用这个进行正常的对接接口开发等。