PWA缓存策略

PWA的特点

  • 渐进式:适用于选用任何浏览器的所有用户,因为它是以渐进式增强作为核心宗旨来开发的。

  • 自适应:适合任何机型:桌面设备、移动设备、平板电脑或任何未来设备。

  • 连接无关性:能够借助于服务工作线程在离线或低质量网络状况下工作。

  • 离线推送:使用推送消息通知,能够让我们的应用像 Native App 一样,提升用户体验。

  • 及时更新:在服务工作线程更新进程的作用下时刻保持最新状态。

  • 安全性:通过 HTTPS 提供,以防止窥探和确保内容不被篡改。

Service Worker生命周期:

  • 注册(register):这里一般指在浏览器解析到JavaScript有注册Service Worker时的逻辑,即调用navigator.serviceWorker.register()时所处理的事情。

  • 安装中( installing ):这个状态发生在 Service Worker 注册之后,表示开始安装。

  • 安装后( installed/waiting ):Service Worker 已经完成了安装,这时会触发install事件,在这里一般会做一些静态资源的离线缓存。如果还有旧的Service Worker正在运行,会进入waiting状态,如果你关闭当前浏览器,或者调用self.skipWaiting(),方法表示强制当前处在 waiting 状态的 Service Worker 进入 activate 状态。

  • 激活( activating ):表示正在进入activate状态,调用self.clients.claim())会来强制控制未受控制的客户端,例如你的浏览器开了多个含有Service Worker的窗口,会在不切的情况下,替换旧的 Service Worker 脚本不再控制着这些页面,之后会被停止。此时会触发activate事件。

  • 激活后( activated ):在这个状态表示Service Worker激活成功,在activate事件回调中,一般会清除上一个版本的静态资源缓存,或者其他更新缓存的策略。这代表Service Worker已经可以处理功能性的事件fetch (请求)、sync (后台同步)、push (推送),message(操作dom)。

  • 废弃状态 ( redundant ):这个状态表示一个 Service Worker 的生命周期结束。

整个流程可以用下图解释:

介绍 stale-while-revalidate=

举例:Cache-control: max-age=10, stale-while-revalidate=60(接口缓存10秒,swr设置为60秒)

mdn解释:表明客户端愿意接受陈旧的响应,同时在后台异步检查新的响应。秒值指示客户愿意接受陈旧响应的时间长度。

可以理解为:当该接口缓存过期后的60秒(60秒为例子中的60)内,再次请求该接口,仍然会立刻返回已经过期的信息,同时也会调用接口向服务器获取最新的数据,并重新保存在缓存中。

为方便理解,stale-while-revalidate可以总结为有三个特性:

swr发生在缓存失效后(max-age后)

swr会在缓存失效后的一定时间内(swr期间)的第一次请求,仍然会返回老数据

swr期间请求接口,浏览器会发请求获取新数据,但不会在本次请求中返回(新数据)

基本流程如下图:

如果在swr期间请求接口:

适用性 swr并不适用于大部分接口

首先swr作为缓存的一种方式,肯定不适合高频更新的内容,如列表数据、详情信息等。

而且swr会在缓存失效后一定时间内仍然返回老数据,虽然只有一次,但因为这个特性,也代表他不适合处理如banner获取、用户信息获取等接口,比如马上新年了,每个在运营的网站基本都会新年当天显示新年快乐的banner,但用户新年当天首次打开页面,却看不到新年祝福,就显得很奇怪。

所以,只适合一些更新频率低,而且对时效性不是很看重的接口,目前想到的只有广告展示这种接口比较适用。 ------------------------------------------------

上文链接:blog.csdn.net/block_xu/ar...

typescript 复制代码
sw.js
/* ===========================================================
 * docsify sw.js
 * ===========================================================
 * Copyright 2016 @huxpro
 * Licensed under Apache 2.0
 * Register service worker.
 * ========================================================== */

const RUNTIME = 'docsify'
const HOSTNAME_WHITELIST = [
    self.location.hostname,
    'fonts.gstatic.com',
    'fonts.googleapis.com',
    'cdn.jsdelivr.net'
]

// The Util Function to hack URLs of intercepted requests
const getFixedUrl = (req) => {
    var now = Date.now()
    var url = new URL(req.url)

    // 1. fixed http URL
    // Just keep syncing with location.protocol
    // fetch(httpURL) belongs to active mixed content.
    // And fetch(httpRequest) is not supported yet.
    url.protocol = self.location.protocol

    // 2. add query for caching-busting.
    // Github Pages served with Cache-Control: max-age=600
    // max-age on mutable content is error-prone, with SW life of bugs can even extend.
    // Until cache mode of Fetch API landed, we have to workaround cache-busting with query string.
    // Cache-Control-Bug: <https://bugs.chromium.org/p/chromium/issues/detail?id=453190>
    if (url.hostname === self.location.hostname) {
        url.search += (url.search ? '&' : '?') + 'cache-bust=' + now
    }
    return url.href
}

/**
 *  @Lifecycle Activate
 *  New one activated when old isnt being used.
 *
 *  waitUntil(): activating ====> activated
 */
self.addEventListener('activate', event => {
    event.waitUntil(self.clients.claim())
})

/**
 *  @Functional Fetch
 *  All network requests are being intercepted here.
 *
 *  void respondWith(Promise<Response> r)
 */
self.addEventListener('fetch', event => {
    // Skip some of cross-origin requests, like those for Google Analytics.
    if (HOSTNAME_WHITELIST.indexOf(new URL(event.request.url).hostname) > -1) {
        // Stale-while-revalidate
        // similar to HTTP's stale-while-revalidate: <https://www.mnot.net/blog/2007/12/12/stale>
        // Upgrade from Jake's to Surma's: <https://gist.github.com/surma/eb441223daaedf880801ad80006389f1>
        const cached = caches.match(event.request)
        const fixedUrl = getFixedUrl(event.request)
        const fetched = fetch(fixedUrl, { cache: 'no-store' })
        const fetchedCopy = fetched.then(resp => resp.clone())

        // Call respondWith() with whatever we get first.
        // If the fetch fails (e.g disconnected), wait for the cache.
        // If there's nothing in cache, wait for the fetch.
        // If neither yields a response, return offline pages.
        event.respondWith(
            Promise.race([fetched.catch(_ => cached), cached])
                .then(resp => resp || fetched)
                .catch(_ => { /* eat any errors */ })
        )

        // Update the cache with the version we fetched (only for ok status)
        event.waitUntil(
            Promise.all([fetchedCopy, caches.open(RUNTIME)])
                .then(([response, cache]) => response.ok && cache.put(event.request, response))
                .catch(_ => { /* eat any errors */ })
        )
    }
})
xml 复制代码
  <!-- 实现离线化 -->
  <script>
    if (typeof navigator.serviceWorker !== 'undefined') {
      navigator.serviceWorker.register('pwa.js')
    }
  </script>

除了 Stale-while-revalidate 策略外,还有一些常见的缓存策略,具体选择取决于应用的需求和性能考虑。以下是一些常见的缓存策略:

  1. Cache First(缓存优先):
  • 首先尝试从缓存中获取数据,如果缓存中有匹配的数据,则直接返回给页面,不发起网络请求。
  • 适用于静态资源或相对不频繁变化的数据。
  1. Network First(网络优先):

    • 首先尝试通过网络请求获取最新数据,如果网络请求成功,则返回最新数据,否则回退到缓存中的数据。
    • 适用于对实时性要求较高的数据。
  2. Cache Only(仅使用缓存):

    • 仅使用缓存中的数据,不发起网络请求。
    • 适用于完全离线可用的应用或对实时性要求较低的场景。
  3. Network Only(仅使用网络):

    • 忽略缓存,直接通过网络请求获取数据。
    • 适用于对实时性要求极高的场景,且可以忽略缓存的情况。
  4. Cache and Network Race(缓存和网络竞速):

    • 同时发起缓存和网络请求,先返回先完成的响应。
    • 适用于提高加载性能的场景,保证快速展示内容。

选择合适的策略取决于应用的具体需求,例如数据更新频率、对实时性的要求以及网络状况等因素。在一些情况下,还可以根据资源类型采用不同的策略,以优化性能和用户体验。

相关推荐
deli0071 小时前
手机拍灯为什么有一条条黑纹?PWM 240Hz 实测
前端
wangruofeng6 小时前
AI Models 1.1.0 发布,查大模型价格、对比模型和找 API 地址
前端·aigc·ai编程
wangruofeng6 小时前
给前端开发者的 100 个设计参考网站
前端·设计
星栈9 小时前
视频即代码,Remotion 能做什么、不能做什么
前端·音视频开发
广州华水科技9 小时前
单北斗GNSS位移监测系统的应用与技术解析
前端
用户47949283569159 小时前
从一句话或代码仓到可交互 HTML:开源 Agent 画图技能 Archify v3.2 实测与选型边界
前端·后端·面试
风骏时光牛马12 小时前
生成一个AI多模态标题,仅输出一条标题,无额外文字、问句、备选、配图、文案等任何附加内容,不需要其他的任务服务 只要标题。
前端
wangruofeng12 小时前
从 GIF 到 AVIF:网页图片格式演进与 2026 年选型指南
前端·架构
清晨Coding13 小时前
为什么用 React Developer Tools 查看组件树这么乱?
前端·react.js
鶴哥只手遮天13 小时前
《Electron 45.2.3 内核解剖》第 4 章 补丁体系:380 个补丁改了什么
前端·electron