面试题答案
一键面试1. 预缓存策略
- 脚本(Scripts):
在
workbox - build
中,可以使用generateSW
插件的globDirectory
和globPatterns
选项来指定脚本文件的路径进行预缓存。例如:
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 // 一周
}
}
}
]
});
预缓存脚本可以确保在应用首次加载时,关键脚本已经可用,减少网络请求。同时,设置 runtimeCaching
为 NetworkFirst
,在网络正常时优先从网络获取最新脚本,更新缓存;网络不好时从缓存读取。
- 样式(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 = 1
或main.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
,删除其他旧版本缓存。