Angular构建时如何为assets及resources文件夹文件添加哈希后缀?
解决Angular CLI 1.0.1构建时assets/resources文件未加哈希的缓存问题
我之前维护Angular 4项目时也碰到过一模一样的问题——Angular CLI 1.x版本默认只会给bundle文件加哈希,assets和自定义的resources目录里的静态文件完全没处理,导致浏览器缓存旧资源,用户看不到更新。下面几个方法亲测有效:
方法一:导出Webpack配置自定义处理(不可逆,但配置完全可控)
Angular CLI 1.x支持通过ng eject命令导出完整的webpack配置文件,之后你可以手动修改资源处理规则:
- 执行导出命令(注意:这个操作不可逆,执行后就不能再用Angular CLI的默认简化构建命令,得用npm scripts里的build命令):
ng eject
- 打开生成的
webpack.config.js,找到处理静态资源的file-loader规则,修改options里的name参数,添加哈希值:
{ test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/, loader: 'file-loader', options: { // [hash]会自动生成基于文件内容的哈希值 name: '[name].[hash].[ext]', outputPath: 'assets/' } }
- 如果你的
resources目录不在默认assets范围内,先在angular-cli.json的apps[0].assets数组里添加"resources",然后在webpack配置里新增对应的file-loader规则,或者调整上面的outputPath和匹配规则,确保resources里的文件也能被处理。
方法二:用ngx-build-plus扩展构建配置(无需eject)
如果不想eject破坏原有CLI工作流,可以用ngx-build-plus这个插件来扩展webpack配置,它兼容Angular 4版本:
- 安装依赖:
npm install ngx-build-plus --save-dev
- 修改
angular-cli.json,把apps[0].builder改成ngx-build-plus:build:
"builder": "ngx-build-plus:build"
- 创建一个额外的webpack配置文件,比如
webpack.extra.js,里面添加资源哈希处理规则:
module.exports = { module: { rules: [ { test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/, loader: 'file-loader', options: { name: '[name].[hash].[ext]', // 根据文件原目录设置输出路径 outputPath: (url, resourcePath) => { if (resourcePath.includes('resources')) { return 'resources/' + url; } return 'assets/' + url; } } } ] } };
- 构建时带上额外配置参数:
ng build --prod --aot --no-sourcemap --output-hash=all --extra-webpack-config webpack.extra.js
方法三:手动写脚本批量添加哈希(适合快速临时解决)
如果不想折腾webpack配置,也可以写个简单的Node.js脚本,在构建完成后自动给assets和resources里的文件加哈希,同时替换index.html里的引用:
- 在项目根目录创建
add-hash.js脚本:
const fs = require('fs'); const path = require('path'); const crypto = require('crypto'); // 计算文件内容的哈希值 function getFileHash(filePath) { const content = fs.readFileSync(filePath); return crypto.createHash('md5').update(content).digest('hex').slice(0, 8); } // 给目录下的文件添加哈希 function processDirectory(dir) { const files = fs.readdirSync(dir); files.forEach(file => { const fullPath = path.join(dir, file); const stats = fs.statSync(fullPath); if (stats.isDirectory()) { processDirectory(fullPath); } else if (stats.isFile()) { const hash = getFileHash(fullPath); const ext = path.extname(file); const baseName = path.basename(file, ext); const newFileName = `${baseName}.${hash}${ext}`; const newFullPath = path.join(dir, newFileName); // 重命名文件 fs.renameSync(fullPath, newFullPath); // 替换index.html里的引用路径 const indexPath = path.join(__dirname, 'dist/index.html'); let indexContent = fs.readFileSync(indexPath, 'utf8'); const relativePath = fullPath.replace(path.join(__dirname, 'dist') + path.sep, ''); const newRelativePath = newFullPath.replace(path.join(__dirname, 'dist') + path.sep, ''); indexContent = indexContent.replace(relativePath, newRelativePath); fs.writeFileSync(indexPath, indexContent); } }); } // 处理assets和resources目录 processDirectory(path.join(__dirname, 'dist/assets')); processDirectory(path.join(__dirname, 'dist/resources'));
- 在
package.json的scripts里添加一个postbuild命令,构建完成后自动执行脚本:
"scripts": { "build": "ng build --prod --aot --no-sourcemap --output-hash=all", "postbuild": "node add-hash.js" }
- 之后直接执行
npm run build就会自动完成哈希添加了。
各方法优缺点对比
- 方法一:完全可控webpack配置,但eject后无法再用Angular CLI的简化命令,适合需要深度定制构建流程的场景。
- 方法二:保留CLI工作流,无需eject,但需要引入第三方插件,要注意版本兼容性。
- 方法三:实现简单,不需要修改构建配置,但脚本需要维护,比如处理嵌套目录、特殊文件类型等情况。
内容的提问来源于stack exchange,提问作者D Deshmane
相关产品推荐
相关产品推荐

