MST
星途 面试题库

面试题:优化React组件在PWA中的资源预缓存与运行时缓存策略

在React构建的大型PWA应用中,资源众多。请详细说明如何针对不同类型的资源(如脚本、样式、图片、字体等),优化预缓存和运行时缓存策略,以提高应用的首次加载速度、离线性能以及在网络波动情况下React组件的稳定性,并且解释如何处理缓存冲突和缓存失效问题。
25.7万 热度难度
前端开发React

知识考点

AI 面试

面试题答案

一键面试

1. 预缓存策略

  • 脚本(Scripts): 在 workbox - build 中,可以使用 generateSW 插件的 globDirectoryglobPatterns 选项来指定脚本文件的路径进行预缓存。例如:
const { generateSW } = require('workbox - build');

generateSW({
  swDest: 'build/service - worker.js',
  globDirectory: 'build',
  globPatterns: ['**/*.js'],
  // 这里可根据脚本特性设置runtimeCaching
  runtimeCaching: [
    {
      urlPattern: /\.(js)$/,
      handler: 'NetworkFirst',
      options: {
        cacheName: 'js - cache',
        expiration: {
          maxEntries: 50,
          maxAgeSeconds: 60 * 60 * 24 * 7 // 一周
        }
      }
    }
  ]
});

预缓存脚本可以确保在应用首次加载时,关键脚本已经可用,减少网络请求。同时,设置 runtimeCachingNetworkFirst,在网络正常时优先从网络获取最新脚本,更新缓存;网络不好时从缓存读取。

  • 样式(Stylesheets): 类似脚本,预缓存样式文件。
generateSW({
  swDest: 'build/service - worker.js',
  globDirectory: 'build',
  globPatterns: ['**/*.css'],
  runtimeCaching: [
    {
      urlPattern: /\.(css)$/,
      handler: 'StaleWhileRevalidate',
      options: {
        cacheName: 'css - cache',
        expiration: {
          maxEntries: 50,
          maxAgeSeconds: 60 * 60 * 24 * 3 // 三天
        }
      }
    }
  ]
});

使用 StaleWhileRevalidate 策略,应用加载时先从缓存读取样式,同时在后台更新缓存,保证样式的快速呈现和及时更新。

  • 图片(Images)
generateSW({
  swDest: 'build/service - worker.js',
  globDirectory: 'build',
  globPatterns: ['**/*.{jpg,png,gif}'],
  runtimeCaching: [
    {
      urlPattern: /\.(jpg|png|gif)$/,
      handler: 'CacheFirst',
      options: {
        cacheName: 'image - cache',
        expiration: {
          maxEntries: 100,
          maxAgeSeconds: 60 * 60 * 24 * 30 // 一个月
        }
      }
    }
  ]
});

对于图片,采用 CacheFirst 策略,优先从缓存读取,因为图片更新频率相对较低,这样可以快速展示图片,减少网络请求。

  • 字体(Fonts)
generateSW({
  swDest: 'build/service - worker.js',
  globDirectory: 'build',
  globPatterns: ['**/*.{woff,woff2}'],
  runtimeCaching: [
    {
      urlPattern: /\.(woff|woff2)$/,
      handler: 'CacheFirst',
      options: {
        cacheName: 'font - cache',
        expiration: {
          maxEntries: 30,
          maxAgeSeconds: 60 * 60 * 24 * 30 // 一个月
        }
      }
    }
  ]
});

字体也使用 CacheFirst 策略,确保字体的稳定加载,避免因网络问题导致字体样式丢失。

2. 运行时缓存策略

  • 脚本:如上述,NetworkFirst 策略可在网络良好时获取最新脚本,同时更新缓存;网络不佳时使用缓存脚本。
  • 样式StaleWhileRevalidate 策略,能保证样式快速加载并在后台更新缓存。
  • 图片CacheFirst 策略下,若缓存中无图片则发起网络请求,请求成功后更新缓存。
  • 字体:同图片,CacheFirst 策略确保字体稳定加载。

3. 提高首次加载速度

  • 预缓存关键资源:通过分析应用启动所需的关键脚本、样式、图片和字体,优先预缓存这些资源。例如,React应用的核心脚本、初始渲染所需的样式和首屏图片等。
  • Code - splitting:对于脚本,使用React的Code - splitting技术,将代码分割成更小的块,只加载当前需要的部分,减少首次加载的脚本体积。
  • Optimize images:压缩图片,选择合适的图片格式(如WebP),减少图片文件大小,加快图片加载。

4. 离线性能

  • 全面预缓存:尽可能预缓存应用离线运行所需的所有资源,包括脚本、样式、图片、字体以及一些静态数据文件。
  • Fallback strategies:为每种资源设置合理的回退策略。如图片加载失败,可显示一张默认图片;脚本加载失败,可尝试使用缓存版本(如果有)或显示友好的提示信息。

5. React组件在网络波动下的稳定性

  • 使用 Suspense:在React组件中使用 Suspense 组件来处理异步数据加载,如加载脚本、图片等资源。Suspense 可以在资源加载时显示加载指示器,避免组件崩溃。
  • Error boundaries:创建Error boundaries组件来捕获组件树中任何位置的JavaScript错误,防止错误导致整个应用崩溃。在网络波动时,资源加载可能失败,Error boundaries可以处理这些错误并显示合适的提示信息。

6. 处理缓存冲突

  • Versioning:为缓存添加版本号,例如在缓存名称中包含版本信息,如 js - cache - v1。当资源更新时,更新缓存版本号,这样新资源会被缓存到新的缓存空间,避免与旧版本冲突。
  • Cache - busting:在资源URL中添加查询参数或哈希值,如 main.js?v = 1main.js?hash = abc123。这样每次资源更新,URL改变,会触发新的缓存,不会与旧缓存冲突。

7. 处理缓存失效

  • Expiration rules:为不同类型的缓存设置合理的过期时间,如上述在 runtimeCaching 中设置的 expiration 选项。到期后,缓存将被清理,下次请求时会从网络获取新资源并更新缓存。
  • Manual cache updates:在应用更新时,手动清除旧的缓存。可以在Service Worker脚本中添加逻辑,检测到应用更新时,删除旧版本的缓存,重新预缓存新资源。例如:
self.addEventListener('install', event => {
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.filter(cacheName => cacheName.startsWith('my - app - cache - ') && cacheName!== 'my - app - cache - v2')
        .map(cacheName => caches.delete(cacheName))
      );
    })
  );
});

这里假设新版本缓存名称为 my - app - cache - v2,删除其他旧版本缓存。