Grunt自动化构建实战:从配置到优化的完整指南
Grunt 打包时图片路径全乱了,折腾一晚上才搞定
昨天改一个老项目,用的是 Grunt 构建的,本来只是想加个新功能,结果打包完发现所有图片都 404 了。页面上全是裂图,控制台一堆 Failed to load resource。我寻思着又没动图片路径相关配置,怎么就崩了?
一开始我以为是 CDN 配置问题,查了下 Gruntfile.js 里的 cdnify 任务,发现压根没开。那问题肯定出在本地路径处理上。翻了翻 dist 目录,发现图片确实被复制过去了,但 HTML 里引用的路径却是错的 —— 比如原本是 img/logo.png,打包后变成了 dist/img/logo.png,但实际图片在 dist/assets/img/logo.png,路径层级完全对不上。
这里我踩了个坑:我以为是 copy 任务的问题,于是花了一个小时调整 copy:dist 的 cwd 和 dest,结果越调越乱。后来试了下发现,根本不是复制的问题,而是 usemin 在处理 HTML 里的资源引用时,路径映射没对上。
原来 useminPrepare 和 usemin 是两套逻辑
这个项目用的是 grunt-usemin 插件,它分两步走:先用 useminPrepare 分析 HTML,生成临时的 Grunt 配置(比如 cssmin、uglify 的任务),再用 usemin 替换 HTML 中的引用路径。
问题就出在第一步。我看了下 HTML 里的写法:
<!-- build:img /assets/img/logo.png -->
<img src="img/logo.png" alt="logo">
<!-- endbuild -->
这种写法其实是错的。grunt-usemin 并不支持 build:img 这种 block。它只认 css、js,以及默认的 concat。对于图片、字体这些静态资源,它不会自动处理路径。
那为什么以前能跑?我翻了 Git 历史,发现之前项目里所有图片都是直接写绝对路径,比如 /img/logo.png,而构建时通过 copy 把 src/img 复制到 dist/img,所以路径刚好对得上。但这次我为了模块化,把图片挪到了 src/assets/img,复制目标也改成了 dist/assets/img,但 HTML 里还是写的相对路径 img/logo.png,自然就断链了。
三种方案对比,我选了最简单的
我试了三种方式:
- 方案一:给所有图片加
build:img支持。但grunt-usemin官方不支持,得自己写自定义 block,太麻烦。 - 方案二:用
grunt-string-replace批量替换 HTML 中的图片路径。但要维护正则,容易误伤,而且新增图片还得改配置。 - 方案三:统一用绝对路径,并让
copy任务保持目录结构一致。这个最省事。
我果断选了方案三。反正老项目也没用啥 fancy 的模块化,绝对路径反而更稳定。
但光改 HTML 还不够。因为 usemin 在替换 CSS/JS 路径时,会基于 HTML 文件所在位置计算相对路径。如果 HTML 在 dist/ 根目录,而图片在 dist/assets/img/,那引用就得写成 /assets/img/logo.png —— 注意开头的斜杠,表示从根目录开始。
关键来了:usemin 默认不会处理以 / 开头的路径,它只处理相对路径。所以如果你写 src="/assets/img/logo.png",它会原样保留,不会做任何替换或校验。这其实反而是好事,只要你的服务器配置正确,这种路径永远是对的。
核心代码就这几行
最终我的 Gruntfile.js 里做了两处改动:
第一,确保 copy 任务把所有静态资源(包括图片)按原结构复制到 dist:
copy: {
dist: {
files: [{
expand: true,
cwd: 'src',
src: [
'assets/**/*', // 包含 img, fonts, icons 等
'index.html'
],
dest: 'dist'
}]
}
}
第二,HTML 里所有图片、字体等资源,全部使用以 / 开头的绝对路径:
<!-- 正确写法 -->
<img src="/assets/img/logo.png" alt="logo">
<link rel="icon" href="/assets/favicon.ico">
注意:不要用 build 注释包裹这些静态资源,因为 usemin 不处理它们,强行包裹反而可能导致解析错误。
然后,确保 usemin 任务只处理 CSS 和 JS:
useminPrepare: {
html: 'dist/index.html',
options: {
dest: 'dist'
}
},
usemin: {
html: ['dist/index.html'],
css: ['dist/styles/*.css'],
options: {
assetsDirs: ['dist', 'dist/assets']
}
}
这里有个细节:assetsDirs 必须包含所有可能存放静态资源的目录,否则 usemin 在替换 CSS 中的背景图路径时可能会找不到文件。比如 CSS 里有 background: url('../assets/img/bg.jpg'),如果 assetsDirs 没包含 dist/assets,它就无法正确解析这个路径。
踩坑提醒:这三点一定注意
1. 别乱用 build:xxx:除了 css 和 js,其他 block 类型基本不被支持,写了也白写,还可能干扰解析。
2. 绝对路径比相对路径更可靠:在构建工具里,相对路径依赖于文件之间的相对位置,一旦目录结构调整,全盘皆输。而以 / 开头的路径,只要服务器根目录对,就永远正确。
3. assetsDirs 别漏配:这个配置项决定了 usemin 在哪些目录里找资源文件。如果 CSS 引用了图片,但图片所在目录没列在 assetsDirs 里,构建时就会报错说找不到文件。
改完之后重新构建,图片终于正常显示了。虽然还有个小问题:开发时用的本地服务器(比如 grunt serve)可能不支持根路径 /,这时候需要配个 base href 或者用代理。不过这个问题只影响开发环境,生产环境没问题就行,我暂时忍了。
以上是我踩坑后的总结,如果你有更好的方案欢迎评论区交流。说实话,现在新项目我都用 Vite 或 Webpack 了,Grunt 这种老古董真是一碰就掉渣。但没办法,有些遗留项目还得维护,只能硬着头皮修。希望这篇记录能帮到同样在和 Grunt 搏斗的你。

暂无评论