第2章 配置强缓存策略:Cache-Control与Expires头设置、服务端与客户端协同、静态资源版本化
强缓存是WebView缓存体系中最直接、性能收益最高的优化手段。当资源被强缓存命中时,WebView不会向服务端发送任何网络请求,直接从本地缓存加载资源,延迟可降至毫秒级。本章围绕Cache-Control与Expires头的配置、服务端与客户端的协同机制以及静态资源版本化展开,给出可落地的技术实现方案。
2.1 强缓存策略的核心原理
强缓存依赖HTTP响应头中的Cache-Control或Expires字段,浏览器和WebView在加载资源时首先检查本地缓存副本是否在有效期内。如果未过期,则直接使用缓存副本,整个过程零网络开销。
| 指令 | 值示例 | 作用 | 优先级 |
|---|---|---|---|
max-age | 31536000 | 指定资源有效期(秒),相对时间 | 高 |
s-maxage | 31536000 | 公共缓存(CDN)专用有效期 | 最高 |
public | — | 允许所有代理和客户端缓存 | — |
private | — | 仅允许客户端缓存,禁止代理缓存 | — |
no-cache | — | 强制每次使用前向服务端验证(协商缓存) | — |
no-store | — | 禁止所有缓存机制 | — |
immutable | — | 提示浏览器资源永不变化,可安全使用缓存 | 增强 |
优先级规则:
Cache-Control 优先级高于 Expires;s-maxage 高于 max-age;private 与 public 决定缓存作用域。现代WebView全面优先使用 Cache-Control。
Expires 使用时间(如 Expires: Wed, 21 Oct 2025 07:28:00 GMT),依赖客户端时钟,容易因时间偏差导致缓存失效异常。因此推荐统一使用 Cache-Control 替代 Expires,仅在需要兼容HTTP/1.0老旧代理时保留 Expires 作为降级方案。
2.2 服务端配置实现
服务端是强缓存策略的起点。以下分别给出Nginx、Apache和Spring Boot的典型配置。
Nginx 配置示例
location ~* \.(js|css|png|jpg|woff2|svg)$ {
expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Vary "Accept-Encoding";
}
关键点分析:
- expires 1y:自动生成
Expires头(1年后),同时影响Cache-Control: max-age。 - immutable:告知WebView该资源内容永不改变,可跳过条件验证请求,大幅提升二次加载速度。
- Vary:避免内容编码不一致导致缓存错乱。
Apache 配置示例
<FilesMatch "\.(js|css|png|jpg)$">
Header set Cache-Control "public, max-age=31536000, immutable"
Header set Expires "A31536000"
</FilesMatch>
Spring Boot (Java) 示例
@Configuration
public class StaticResourceConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/")
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS)
.cachePublic()
.mustRevalidate());
}
}
部署建议: 对于CSS/JS/图片等不常变更的静态资源,
max-age 建议设置为 1 年(31536000 秒),并配合 immutable 标记。HTML文件建议设置 no-cache 以支持增量更新。
2.3 客户端协同配置
WebView 默认的缓存行为由操作系统网络栈决定,但开发者可以通过配置主动控制缓存策略。以下分 Android 和 iOS 给出实现。
Android WebView 缓存模式配置
WebView webView = new WebView(context);
WebSettings settings = webView.getSettings();
// 优先使用缓存策略:根据 Cache-Control 决定
settings.setCacheMode(WebSettings.LOAD_DEFAULT);
// 离线优先:缓存不存在时才走网络
// settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
// 强制不使用缓存
// settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
// 启用 HTTP 缓存
settings.setAppCacheEnabled(true);
settings.setDatabaseEnabled(true);
settings.setDomStorageEnabled(true);
iOS WKWebView 缓存策略配置
import WebKit
let config = WKWebViewConfiguration()
let url = URL(string: "https://example.com")!
let request = URLRequest(
url: url,
cachePolicy: .returnCacheDataElseLoad,
timeoutInterval: 30
)
// 或使用协议默认策略
// let request = URLRequest(url: url,
// cachePolicy: .useProtocolCachePolicy,
// timeoutInterval: 30)
let webView = WKWebView(frame: .zero, configuration: config)
webView.load(request)
| 平台 | 缓存模式 | 行为描述 | 推荐场景 |
|---|---|---|---|
| Android | LOAD_DEFAULT | 根据 Cache-Control 执行 | 通用线上 |
| Android | LOAD_CACHE_ELSE_NETWORK | 优先本地缓存 | 离线体验 |
| Android | LOAD_NO_CACHE | 完全绕过缓存 | 强实时场景 |
| iOS | useProtocolCachePolicy | 系统默认协议策略 | 通用线上 |
| iOS | returnCacheDataElseLoad | 优先本地缓存 | 离线降级 |
| iOS | reloadIgnoringCacheData | 忽略所有缓存 | 强制刷新 |
注意: 客户端缓存模式调整无法替代服务端响应头配置。若服务端未返回
Cache-Control,即使客户端设置 LOAD_DEFAULT 也不会产生强缓存效果。两端配置必须协同一致。
2.4 静态资源版本化策略
静态资源版本化是强缓存策略的“安全锁”。通过修改资源URL来强制WebView加载新版本,避免因长缓存导致用户无法获取更新。
三种主流版本化方案对比
| 策略 | 实现方式 | 优点 | 缺点 |
|---|---|---|---|
| 文件名哈希 | app.8f3b2c1a.js | 内容变更URL必变,缓存失效彻底 | 部署后需同步更新引用 |
| 版本号参数 | app.js?v=2.3.1 | 实施简单,无需改文件名 | 代理/CDN可能忽略参数 |
| 路径版本化 | /v2/js/app.js | 结构清晰,便于灰度 | 版本迁移成本较高 |
推荐方案:文件名哈希 + 构建工具自动注入。以下为 webpack 的典型配置:
// webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: '[name].[contenthash:8].js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/static/'
},
plugins: [
// 自动将哈希文件名注入 HTML
new HtmlWebpackPlugin({
template: './src/index.html'
})
]
};
生成效果示例:
<!-- 构建前 -->
<script src="/static/app.js"></script>
<!-- 构建后 -->
<script src="/static/app.8f3b2c1a.js"></script>
版本化与强缓存的协同逻辑: 资源文件名随内容哈希变化 → WebView 视为全新资源 → 服务端返回新的
Cache-Control 头 → 强缓存策略对新资源生效。旧资源在缓存自然淘汰或手动清理后释放。
2.5 调试方法与验证
强缓存策略是否正确生效,需要通过浏览器开发工具或抓包工具验证。
- Chrome DevTools → Network 面板: 查看
Size列显示(disk cache)或(memory cache)表示强缓存命中;Status为200 (from cache)。 - 响应头检查: 确认
Cache-Control和Expires字段值与服务端配置一致。 - 版本化验证: 更新资源后确认HTML中引用URL的哈希值发生变化,且网络请求为新URL。
- 禁用缓存测试: 在 DevTools 中勾选
Disable cache模拟首次访问,验证完整请求链路。
# 使用 curl 验证响应头
curl -I https://example.com/static/app.8f3b2c1a.js
# 预期返回
# HTTP/2 200
# cache-control: public, max-age=31536000, immutable
# expires: Thu, 01 Jan 2026 00:00:00 GMT
2.6 性能与稳定性优化
- 缓存命中率监控: 通过 performance API 或 WebView 的
loadResource回调统计缓存命中率,目标 > 90%。 - 存储配额管理: Android 默认缓存上限为应用可用空间的 1/1000,iOS 为 50MB。超限时自动淘汰最早资源。
- 紧急版本回退: 当线上出现严重Bug时,可通过服务端下发
Cache-Control: no-cache使关键资源立即失效。 - 避免过度缓存: HTML、API 响应等动态资源不适合长缓存,应设置
no-cache或短max-age(如 60 秒)。 - CDN 缓存协同: 若使用 CDN,需确保
s-maxage与max-age合理配合,避免 CDN 缓存与客户端缓存不一致。
生产环境最佳实践: 将
Cache-Control: public, max-age=31536000, immutable 与文件名哈希配合使用,可实现“发布后秒级更新,更新前零网络请求”的理想缓存效果。HTML文档使用 no-cache 确保每次加载都获取最新资源列表。