🔥产品:网络不给力,前端页面就不能展示了吗?

15,253 阅读9分钟

引言:产品说:“网络不好,没有网络就不能展示页面了吗?” 前端开发人员****

你是否曾经因为网络不稳定而苦恼?是否希望你的网页在离线时依然能流畅运行?今天,我们将探讨如何利用 Workbox 来优化你的 Service Worker,提升网页性能,确保你的 Web 应用在任何环境下都能稳定运行。

image.png

目前您可能还用不到这篇文章,不过可以先收藏起来。希望将来它能为您提供所需的帮助!

🎈什么是 Service Worker?

在深入了解 Workbox 之前,了解 Service Worker 的基础知识是必要的。Service Worker是运行在浏览器背后的独立线程,用于拦截和处理网络请求、管理缓存和处理推送通知。它的主要目的是增强 Web 应用的离线体验和性能。

Service Worker 在用户访问网页时被安装,并在后台运行,能够在不干扰主页面的情况下执行任务。

Service Worker 的生命周期分为以下3个阶段:

1、安装(Install):当浏览器发现新的 Service Worker 脚本时,会触发 install 事件。在这个阶段,开发者通常会缓存应用的静态资源。

self.addEventListener('install', event => {
    event.waitUntil(
        caches.open('my-cache').then(cache => {
            return cache.addAll([
                '/',
                '/index.html',
                '/styles.css',
                '/script.js'
            ]);
        })
    );
});

2、激活(Activate):安装完成后,Service Worker 会进入激活阶段。在这个阶段,开发者可以清理旧的缓存,确保新的缓存策略生效。

self.addEventListener('activate', event => {
    event.waitUntil(
        caches.keys().then(cacheNames => {
            return Promise.all(
                cacheNames.map(cacheName => {
                    if (cacheName !== 'my-cache') {
                        return caches.delete(cacheName);
                    }
                })
            );
        })
    );
});

3、运行(Running):激活后,Service Worker 进入运行状态,开始拦截和处理网络请求。开发者可以通过 fetch 事件来控制请求的处理方式。

self.addEventListener('fetch', event => {
    event.respondWith(
        caches.match(event.request).then(response => {
            return response || fetch(event.request);
        })
    );
});

Service Worker主要应用于以下几个场景:

  1. 离线支持:通过缓存静态资源和动态内容,确保应用在没有网络连接时仍然可以使用。
  2. 缓存管理:提高应用性能,通过缓存减少网络请求次数和加快页面加载速度。
  3. 推送通知:Service Worker 可以处理推送通知,即使用户没有打开应用也能接收消息。

手动编写 Service Worker 也可以实现,但是过于复杂,现在有一款比较成熟 npm 库 Workbox。

为什么选择 Workbox?🔥🔥

Workbox 功能强大特性

Workbox 是由 Google 开发的一组库和工具,帮助开发者轻松地将 Service Worker 集成到他们的 Web 应用中。它简化了缓存管理和离线支持,使得 Web 应用能够在没有网络连接的情况下依然保持高性能和可靠性。

主要用途包括:

  • 简化缓存管理:通过预定义的策略自动处理资源的缓存。
  • 提高性能:加快资源加载速度,减少服务器负载。
  • 增强离线功能:确保应用在离线状态下仍能正常运行。
  • 提供插件与扩展:通过各种插件轻松扩展功能,如背景同步、Google Analytics集成等。

与手动编写 Service Worker 的对比,使用 Workbox,开发者只需配置即可,大大提升了开发效率。经过广泛测试和社区验证,提供了高可靠性和性能优化的默认实现。

引入 Workbox(最简单的方法)

引入 Workbox(最简单的方法)可以通过以下几个步骤完成。Workbox 是一个强大的库,用于帮助开发人员简化构建进程中的离线支持和缓存策略。以下是最简单的方法来引入 Workbox:

1. 使用 Workbox CLI

Workbox CLI 是最快速且简单的方式来引入 Workbox 并生成 Service Worker 文件。

  1. 安装 Workbox CLI

    你需要全局安装 Workbox CLI 工具。打开终端并运行以下命令:

    npm install -g workbox-cli
    
  2. 初始化 Workbox

    在你的项目根目录下,运行以下命令来初始化 Workbox:

    workbox wizard
    

    这会引导你完成配置过程,并生成一个 workbox-config.js 文件。

  3. 生成 Service Worker

    运行以下命令生成 Service Worker 文件:

    workbox generateSW workbox-config.js
    

    这将创建一个 service-worker.js 文件,你需要将它注册到你的应用中。

  4. 注册 Service Worker

    在你的应用入口文件(例如 index.jsmain.js)中添加以下代码来注册 Service Worker:

    if ('serviceWorker' in navigator) {
      window.addEventListener('load', () => {
        navigator.serviceWorker.register('/service-worker.js').then(registration => {
          console.log('SW registered: ', registration);
        }).catch(registrationError => {
          console.log('SW registration failed: ', registrationError);
        });
      });
    }
    

2. 使用 Workbox CDN

如果你不想使用 CLI 工具,你也可以直接通过 CDN 引入 Workbox:

  1. 在 HTML 文件中添加 Workbox 的 CDN 链接

    在你的 HTML 文件的 <head> 标签中添加以下代码:

    <script src="https://storage.googleapis.com/workbox-cdn/releases/6.4.1/workbox-sw.js"></script>
    
  2. 创建和注册 Service Worker

    创建一个 service-worker.js 文件并添加以下代码:

    importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.4.1/workbox-sw.js');
    
    workbox.precaching.precacheAndRoute(self.__WB_MANIFEST);
    

    然后在你的应用入口文件中注册这个 Service Worker:

    if ('serviceWorker' in navigator) {
      window.addEventListener('load', () => {
        navigator.serviceWorker.register('/service-worker.js').then(registration => {
          console.log('SW registered: ', registration);
        }).catch(registrationError => {
          console.log('SW registration failed: ', registrationError);
        });
      });
    }
    
  3. 使用 Workbox 插件(可选)

    你可以根据需要添加其他 Workbox 插件,例如缓存策略:

    workbox.routing.registerRoute(
      ({request}) => request.destination === 'image',
      new workbox.strategies.CacheFirst({
        cacheName: 'images',
        plugins: [
          new workbox.expiration.ExpirationPlugin({
            maxEntries: 50,
            maxAgeSeconds: 30 * 24 * 60 * 60, // 30 天
          }),
        ],
      })
    );
    

Workbox配置

Workbox 预缓存功能

workbox.precaching 对象提供了常用的预缓存功能,其中最常用的方法是 workbox.precaching.precacheAndRoute。它的作用跟我们前面实现的 Precacher.precacheAndRoute() 的功能类似, 都是将传入的资源列表进行预缓存,同时对匹配到的预缓存请求直接从本地缓存中读取并返回。

workbox.routing.precacheAndRoute([
  {
    url: '/index.html',
    revision: 'asdf'
  },
  '/index.abc.js',
  '/index.bcd.css'
])

Workbox 路由功能全面解析

Workbox 对资源请求匹配和对应的缓存策略执行进行了统一管理,采用路由注册的组织形式,以此来规范化动态缓存。Workbox 提供了简洁的 workbox.routing.registerRoute 方法来注册路由,规范化动态缓存操作。基本用法如下:

workbox.routing.registerRoute(match, handlerCb)

🐱路由匹配规则详解

workbox.routing.registerRoute 的第一个参数 match 是路由匹配规则,支持以下几种匹配模式:

  1. 字符串匹配:对资源 URL 进行字符串匹配。无论是完整 URL 还是相对路径,都可以匹配到相应资源。
workbox.routing.registerRoute('http://127.0.0.1:8080/index.css', handlerCb)
workbox.routing.registerRoute('/index.css', handlerCb)
workbox.routing.registerRoute('./index.css', handlerCb)

例如,以上注册的路由都能匹配到 http://127.0.0.1:8080/index.css

  1. 正则匹配:对资源 URL 进行正则匹配。
workbox.routing.registerRoute(//index.css$/, handlerCb)

这个规则可以匹配以下同域资源:

  • http://127.0.0.1:8080/index.css
  • http://127.0.0.1:8080/a/index.css

但无法匹配跨域资源。为匹配跨域资源需明确指定域名:

workbox.routing.registerRoute(
  /^https://third-party-site.com/.*/index.css$/,
  handlerCb
)
  1. 自定义方法匹配:根据需求实现复杂的资源请求匹配规则。
const match = ({url, event}) => {
  return url.pathname === '/index.html'
}

match 方法接收 urlevent 参数,其中 urlURL 类实例,eventfetch 事件回调参数。

🐱资源请求处理方法

第二个参数 handlerCb 决定如何响应匹配到的请求,可以从网络、缓存获取资源或在 Service Worker 中直接生成响应。

const handlerCb = ({url, event, params}) => {
  return Promise.resolve(new Response('Hello World!'))
}

handlerCb 方法接收的对象包含以下属性:

  • url:经过 URL 类实例化的 event.request.url
  • eventfetch 事件回调参数。
  • params:自定义匹配方法返回的值。

注意handlerCb 必须是异步函数,返回一个 Promise,该 Promise 的解析结果必须是一个 Response 对象。

Workbox 的路由功能通过统一管理资源请求匹配和缓存策略,显著提升了前端开发的效率和用户体验。

Workbox缓存策略(🔥🔥核心点,非常重要)

Workbox 提供了一系列灵活且强大的缓存策略,帮助开发者优化Web应用的性能。以下是几种常用的缓存策略以及它们的详细说明和应用场景:

  • NetworkFirst:网络优先
  • CacheFirst:缓存优先
  • NetworkOnly:仅使用正常的网络请求
  • CacheOnly:仅使用缓存中的资源
  • StaleWhileRevalidate:从缓存中读取资源的同时发送网络请求更新本地缓存
const {NetworkFirst, CacheFirst, StaleWhileRevalidate} = workbox.strategies;

workbox.routing.registerRoute(/\api/, new workbox.strategies.NetworkFirst())

Stale-while-revalidate

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

  1. 在第一次请求获取资源时,从网络中提取资源,将其放入缓存中并返回网络响应。
  2. 对于后续请求,首先从缓存提供资源,然后“在后台”从网络重新请求该资源,并更新资源的缓存条目。
  3. 对于此后的请求,您将收到在上一步中从缓存中放置的最后一个网络提取的版本。

image.png

Cache First

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

image.png

Network First

优先尝试拿到网络请求的返回结果,如果拿到网络请求的结果,就将结果返回给客户端并且写入 Cache 缓存,如果网络请求失败,那最后被缓存的 Cache 缓存结果就会被返回到客户端,这种策略一般适用于返回结果不太固定或对实时性有要求的请求,为网络请求失败进行兜底。

image.png

Cache Only

始终从缓存中获取资源,不发起网络请求。

image.png

Network Only

始终从网络请求资源,不使用缓存。

image.png

扩展延伸:Workbox自定义策略

在某些情况下,您可能希望使用自己的其他策略来响应请求,或者只是通过模板在 Service Worker 中生成请求。
为此可以提供一个异步返回 Response 对象的函数 handler 。

const handler = async ({ url, event }) => {
  return new Response(`Custom handler response.`);
};

workbox.routing.registerRoute(new RegExp(matchString), handler);

需要注意的是,如果在 match 回调中返回一个值,它将 handler 作为 params 参数传递到回调中。

const match = ({ url, event }) => {
  if (url.pathname === '/example') {
    return {
      name: 'Workbox',
      type: 'guide',
    };
  }
};

const handler = async ({ url, event, params }) => {
  // Response will be "A guide to Workbox"
  return new Response(`A ${params.type} to ${params.name}`);
};

workbox.routing.registerRoute(match, handler);

如果 URL 中的某些信息可以在 match 回调中解析一次并在中使用,则这可能会对 handler 有所帮助。

总结

希望这篇文章对你理解使用 Workbox 功能有所帮助。如果你有任何疑问或见解,欢迎在评论区分享或提问,让我们共同进步!

你打算如何在项目中处理网络不给力的情况呢?期待你的留言!