JackAtlas

Command Palette

Search for a command to run...

第四章 拦截网络请求 - 《Progressive Web Apps》读书笔记四
over 7 years ago
前端开发

第四章 拦截网络请求 - 《Progressive Web Apps》读书笔记四

本章会深入讨论 fetch 事件并展示它所提供的诸多用例。

4.1 Fetch API

传统上,从服务器获取数据是通过使用 XMLHttpRequest 对象,也成为 AJAX。

var request
if (window.XMLHttpRequest) {
  request = new XMLHttpRequest()
} else {
  try {
    request = new ActiveXObject('Msxml2.XMLHTTP')
  } catch (e) {
    try {
      request = new ActiveXObject('Microsoft.XMLHTTP')
    } catch (e) {}
  }
}

使用 XMLHttpRequest 对象来编写代码会相当棘手,逻辑越复杂,代码就越复杂。

Fetch API 是 Service Worker 全局作用域的一部分,可以用来在任何 Service Worker 中发起 HTTP 请求。

fetch('/some/url', {
  method: 'POST',
  headers: {
    'auth': '1234'
  },
  body: JSON.stringify({
    name: 'dean',
    login: 'dean123'
  })
})
.then(function (data) {
  console.log('Request success: ', data)
})
.catch(function (error) {
  console.log('Request failure: ', error)
})

4.2 fetch 事件

在 Service Worker 中创建自定义 HTTP 响应

self.addEventListener('fetch', function (event) {
  if (/\.jpg$/.test(event.request.url)) {
    event.respondWith(
      new Response('<p>This is a response that comes from your Service Worker!</p>', { header: 'Content-Type': 'text/html' })
    )
  }
})

Service Worker 生命周期

前文说过,只有当 Service Worker 安装完成并且用户刷新了页面或者跳转到网站的其他页面时,Service Worker 才会激活并开始拦截请求。

self.addEventListener('install', function (event) {
  event.waitUntil(self.skipWaiting())
})

skipWaiting() 函数强制等待中的 Service Worker 成为激活的 Service Worker。该函数还可以与 self.clients.claim() 一起使用,以确保底层 Service Worker 的更新立即生效。

self.addEventListener('activate', function (event) {
  event.waitUntil(self.clients.claim())
})

4.3 fetch 实战

4.3.1 使用 WebP 图片的示例

支持 WebP 格式的浏览器会在每个 HTTP 请求中添加 accept: image/webp 请求头来告知服务端它支持 WebP 格式。现在 Service Worker 可以拦截请求,将 WebP 图片进行返回。

self.addEventListener('fetch', function (event) {
  if (/\.jpg$|.png$/.test(event.request.url)) {
    var supportsWebp = false
    if (event.request.headers.has(accept)) {
      supportsWebp = event.request.headers.get('accept').includes('webp')
    }

    if (supportsWebp) {
      var req = event.request.clone()

      var returnUrl = req.url.substr(0, req.url.lastIndexOf('.')) + '.webp'

      event.resondWith(
        fetch(returnUrl, {
          mode: 'no-cors'
        })
      )
    }
  }
})

4.3.2 使用 Save-Data 请求头的示例

使用最新版本浏览器,有一个节省流量的选项,启用该功能则会为每个 HTTP 请求添加一个新的请求头 saveData。

self.addEventListener('fetch', function (event) {
  if (event.request.headers.get('save-data')) {
    // 为了节省流量限制了图标和字体
    if (event.request.url.includes('fonts.googleapis.com')) {
      // 不返回任何内容
      event.respondWith(new Response('', { status: 417, statusText: 'Ignore fonts to save data.' }))
    }
  }
})