Appearance
Webpack 缓存策略
缓存策略是 Webpack 性能优化的重要组成部分,合理的缓存配置可以显著提升构建速度和用户体验。本章将详细介绍各种缓存策略的配置和使用。
构建缓存
文件系统缓存
javascript
// Webpack 5 的文件系统缓存
module.exports = {
cache: {
type: 'filesystem', // 启用文件系统缓存
buildDependencies: {
config: [__filename] // 配置文件变更时清除缓存
},
cacheDirectory: path.resolve(__dirname, '.webpack-cache'), // 缓存目录
name: 'production', // 缓存名称,用于区分不同环境
version: '1.0.0' // 缓存版本,变更时清除缓存
}
};
// 开发环境配置
module.exports = {
mode: 'development',
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
},
name: 'development'
}
};
缓存优化配置
javascript
module.exports = {
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
},
// 缓存压缩
compression: 'brotli', // 'gzip' 或 'brotli'
// 缓存限制
maxMemoryGenerations: 10, // 内存中保留的最大生成次数
maxAge: 1000 * 60 * 60 * 24 * 30 // 缓存有效期(30天)
},
// 快照配置
snapshot: {
managedPaths: [ // Webpack 监控的路径
path.resolve(__dirname, 'node_modules')
],
immutablePaths: [ // 不变路径
path.resolve(__dirname, 'immutable-deps')
],
resolveBuildDependencies: {
hash: true, // 启用构建依赖哈希
timestamp: true // 启用时间戳
},
resolve: {
hash: true,
timestamp: true
},
module: {
hash: true,
timestamp: true
}
}
};
模块标识符优化
确定性模块 ID
javascript
module.exports = {
optimization: {
moduleIds: 'deterministic', // 确定性模块 ID
chunkIds: 'deterministic', // 确定性 chunk ID
runtimeChunk: 'single' // 单独的运行时 chunk
}
};
// 不同的模块 ID 策略
module.exports = {
optimization: {
// moduleIds: 'named', // 开发环境推荐
// moduleIds: 'size', // 按大小优化
// moduleIds: 'total-size', // 按总大小优化
moduleIds: 'deterministic' // 生产环境推荐
}
};
长期缓存优化
javascript
module.exports = {
optimization: {
moduleIds: 'deterministic',
chunkIds: 'deterministic',
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10,
chunks: 'all'
}
}
}
},
output: {
filename: '[name].[contenthash].js', // 使用内容哈希
chunkFilename: '[name].[contenthash].chunk.js',
path: path.resolve(__dirname, 'dist'),
clean: true
}
};
资源缓存
HTTP 缓存配置
javascript
// 输出配置支持长期缓存
module.exports = {
output: {
filename: '[name].[contenthash:8].js', // 固定长度的哈希
chunkFilename: '[name].[contenthash:8].chunk.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/',
clean: true
},
// 设置资源提示
plugins: [
new webpack.HashedModuleIdsPlugin() // Webpack 4 使用
]
};
服务端缓存配置
javascript
// 结合服务器配置实现最佳缓存
module.exports = {
output: {
filename: '[name].[contenthash:8].js',
chunkFilename: '[name].[contenthash:8].chunk.js',
path: path.resolve(__dirname, 'dist'),
publicPath: 'https://cdn.example.com/assets/'
},
plugins: [
// 生成资源映射
new WebpackManifestPlugin({
fileName: 'manifest.json'
}),
// 生成子资源完整性 (SRI)
new SriPlugin({
hashFuncNames: ['sha256', 'sha384']
})
]
};
开发环境缓存
开发服务器缓存
javascript
module.exports = {
devServer: {
static: {
directory: path.join(__dirname, 'public'),
watch: {
ignored: [
path.join(__dirname, 'node_modules'),
path.join(__dirname, '.git'),
path.join(__dirname, 'dist')
]
}
},
hot: true, // 启用热模块替换
client: {
progress: true, // 显示进度
overlay: {
errors: true, // 显示错误覆盖
warnings: false // 不显示警告覆盖
}
}
},
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
},
name: 'dev-server'
}
};
开发构建缓存
javascript
// 开发环境构建缓存配置
module.exports = {
mode: 'development',
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
},
name: 'development-build',
version: process.env.NODE_ENV + (process.env.ASSET_VERSION || '')
},
optimization: {
moduleIds: 'named', // 开发环境使用命名 ID 便于调试
chunkIds: 'named'
},
devtool: 'eval-cheap-module-source-map' // 快速源映射
};
Babel 缓存
Babel 缓存配置
javascript
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true, // 启用 Babel 缓存
cacheCompression: false, // 禁用缓存压缩(加快读取)
compact: true // 压缩输出
}
},
exclude: /node_modules/
}
]
}
};
// 高级 Babel 缓存配置
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: path.resolve(__dirname, '.babel-cache'),
cacheIdentifier: JSON.stringify({
env: process.env.NODE_ENV,
babel: require('@babel/core/package.json').version
}),
cacheCompression: false
}
},
exclude: /node_modules/
}
]
}
};
TypeScript 缓存
TypeScript 缓存配置
javascript
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
module.exports = {
module: {
rules: [
{
test: /\.tsx?$/,
use: [
{
loader: 'ts-loader',
options: {
transpileOnly: true, // 只转译,不进行类型检查
happyPackMode: true, // 启用 happypack 模式
compilerOptions: {
noEmit: false
}
}
}
],
exclude: /node_modules/
}
]
},
plugins: [
// 使用 ForkTsCheckerWebpackPlugin 进行类型检查
new ForkTsCheckerWebpackPlugin({
typescript: {
configFile: path.resolve(__dirname, 'tsconfig.json'),
build: true, // 启用增量编译
mode: 'write-references' // 写入引用文件以加速类型检查
}
})
]
};
缓存失效策略
智能缓存失效
javascript
// 自定义缓存失效逻辑
class CustomCachePlugin {
apply(compiler) {
compiler.hooks.beforeCompile.tapPromise('CustomCachePlugin', async (params) => {
// 检查是否有需要清除缓存的条件
const shouldClearCache = await this.checkCacheInvalidation();
if (shouldClearCache) {
await this.clearCache();
}
});
}
async checkCacheInvalidation() {
// 检查特定条件来决定是否清除缓存
// 例如:检查依赖版本、环境变量等
return process.env.CLEAR_CACHE === 'true';
}
async clearCache() {
// 清除缓存的逻辑
const cacheDir = path.resolve(__dirname, '.webpack-cache');
if (fs.existsSync(cacheDir)) {
await fs.promises.rm(cacheDir, { recursive: true, force: true });
}
}
}
module.exports = {
plugins: [
new CustomCachePlugin()
]
};
环境感知缓存
javascript
// 根据环境使用不同的缓存策略
const isDevelopment = process.env.NODE_ENV === 'development';
const isProduction = process.env.NODE_ENV === 'production';
const cacheConfig = isDevelopment
? {
type: 'filesystem',
buildDependencies: { config: [__filename] },
name: 'development',
version: `${process.env.NODE_ENV}-${Date.now()}`
}
: {
type: 'filesystem',
buildDependencies: { config: [__filename] },
name: 'production',
version: process.env.BUILD_VERSION || '1.0.0',
compression: 'brotli'
};
module.exports = {
cache: cacheConfig,
optimization: {
moduleIds: isProduction ? 'deterministic' : 'named',
chunkIds: isProduction ? 'deterministic' : 'named'
}
};
缓存监控和分析
缓存性能监控
javascript
class CacheStatsPlugin {
apply(compiler) {
let startTime = 0;
compiler.hooks.beforeCompile.tap('CacheStatsPlugin', () => {
startTime = Date.now();
});
compiler.hooks.done.tap('CacheStatsPlugin', (stats) => {
const buildTime = Date.now() - startTime;
const cacheHits = stats.compilation.fileDependencies.size;
console.log(`Build time: ${buildTime}ms`);
console.log(`Cache hits: ${cacheHits}`);
// 输出缓存统计信息
if (compiler.cache && compiler.cache.hooks) {
// Webpack 5 缓存统计
console.log('Using filesystem cache');
}
});
}
}
module.exports = {
plugins: [
new CacheStatsPlugin()
]
};
缓存大小管理
javascript
// 管理缓存大小
const fs = require('fs').promises;
const path = require('path');
class CacheSizePlugin {
constructor(options = {}) {
this.cacheDir = options.cacheDir || '.webpack-cache';
this.maxSize = options.maxSize || 500 * 1024 * 1024; // 500MB
}
async apply(compiler) {
compiler.hooks.done.tapPromise('CacheSizePlugin', async () => {
await this.manageCacheSize();
});
}
async manageCacheSize() {
const cachePath = path.resolve(this.cacheDir);
try {
const stats = await fs.stat(cachePath);
if (stats.size > this.maxSize) {
// 清理旧缓存或执行其他管理操作
console.log(`Cache size (${stats.size}) exceeds limit (${this.maxSize})`);
// 可以在这里实现缓存清理逻辑
}
} catch (error) {
// 缓存目录不存在
}
}
}
高级缓存策略
多级缓存
javascript
// 实现多级缓存策略
module.exports = {
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
},
name: 'primary-cache',
store: 'pack', // 'pack' 或 'json'
compression: 'brotli'
},
// 结合其他缓存策略
optimization: {
moduleIds: 'deterministic',
chunkIds: 'deterministic',
sideEffects: false,
usedExports: true
}
};
CI/CD 缓存集成
javascript
// CI/CD 环境下的缓存配置
const isCI = process.env.CI === 'true' || process.env.CONTINUOUS_INTEGRATION === 'true';
module.exports = {
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
},
name: isCI ? 'ci-cache' : 'local-cache',
version: process.env.GIT_COMMIT || 'local',
store: 'pack'
},
// CI 环境可能需要不同的优化策略
optimization: isCI ? {
minimize: false, // CI 环境可能不需要压缩
moduleIds: 'named',
chunkIds: 'named'
} : {
minimize: true,
moduleIds: 'deterministic',
chunkIds: 'deterministic'
}
};
缓存配置最佳实践
完整的缓存配置示例
javascript
const path = require('path');
module.exports = {
mode: process.env.NODE_ENV,
// 文件系统缓存
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
},
name: process.env.NODE_ENV,
version: process.env.BUILD_VERSION || '1.0.0',
cacheDirectory: path.resolve(__dirname, '.webpack-cache'),
maxMemoryGenerations: 10,
maxAge: 1000 * 60 * 60 * 24 * 7 // 7天
},
// 优化配置
optimization: {
moduleIds: process.env.NODE_ENV === 'production' ? 'deterministic' : 'named',
chunkIds: process.env.NODE_ENV === 'production' ? 'deterministic' : 'named',
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10,
chunks: 'all'
}
}
}
},
// 输出配置(支持长期缓存)
output: {
filename: process.env.NODE_ENV === 'production'
? '[name].[contenthash:8].js'
: '[name].js',
chunkFilename: process.env.NODE_ENV === 'production'
? '[name].[contenthash:8].chunk.js'
: '[name].chunk.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/',
clean: true
},
// 模块配置
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
cacheCompression: false
}
},
exclude: /node_modules/
}
]
},
// 快照配置
snapshot: {
managedPaths: [path.resolve(__dirname, 'node_modules')],
immutablePaths: [path.resolve(__dirname, 'immutable-deps')]
}
};
小结
Webpack 缓存策略是提升构建性能的关键。通过合理配置文件系统缓存、模块标识符、资源缓存等,可以显著减少构建时间。在生产环境中,使用确定性模块 ID 和内容哈希可以实现长期缓存,提升用户体验。选择合适的缓存策略需要根据项目特点和部署环境来决定。