对比 compact、files、expand 三种文件映射写法优缺点
解读
在真实的中国前端团队面试里,这道题考察的不是“会不会写”,而是“为什么这样写”。
面试官想确认三件事:
- 你是否理解 Grunt 的“文件对象格式”与“文件数组格式”本质差异;
- 能否根据项目规模、团队协作、可维护性快速选型;
- 是否踩过“Windows 路径分隔符”“多层级目录”“通配符爆炸”这些国产坑。
答出“expand 支持动态插值,compact 适合一次性任务,files 数组可读性最好”只能算 60 分,必须结合国内 CI 环境、多人协作、代码审查场景给出权衡才算通关。
知识点
-
compact(简洁对象写法)
语法:dest: src,一个键一个值。
底层:被 Grunt 直接解析为“文件数组格式”的特例,不支持额外属性。 -
files 数组写法
语法:[{dest: 'xxx', src: ['xxx'], filter: ..., nonull: true, ...}]
底层:grunt.file.expandMapping 的输入,支持逐条配置 filter、dot、nonull、cwd 等全部选项。 -
expand 动态展开
语法:在 files 数组元素里加 expand: true,再配 cwd、src、dest、rename、ext、extDot、flatten 等。
底层:grunt.file.expand + grunt.file.copy 的封装,运行时动态生成文件映射,可插值 <%= %>。 -
国内常见差异点
- Windows 开发机+Linux Docker 构建:compact 里硬编码路径分隔符会 404;expand 用 cwd/src 可自动归一化。
- Webpack 并行打包迁移:老项目先用 Grunt 过渡,expand 的 rename 函数可直接输出 Webpack entry 对象,降低迁移成本。
- 代码审查:MR 里 30 条 compact 一行写法,reviewer 无法快速 diff;files 数组每项独占一行,变更点一目了然。
答案
compact 写法
优点:
- 语法极简,脚本行数最少,适合一次性本地压缩、图片优化等“配置即遗忘”任务;
- 与早期中文教程、阿里规约文档示例一致,老项目接手成本最低。
缺点: - 不支持 filter、cwd、rename,一旦输出目录结构变化必须重写;
- 当 src 通配符匹配 0 文件时默认静默跳过,CI 中容易“假绿”;
- 无法对单条映射做额外处理,多人协作时可读性差。
files 数组写法
优点:
- 每条映射独立对象,可混用 filter、nonull、dot,精准控制;
- MR 审查时逐行 diff 清晰,符合腾讯、字节等国内大厂的代码规范;
- 支持动态 src:src: ['<%= config.temp %>/**/*.js'],解决“输出目录名随分支变化”场景。
缺点: - 重复 dest/src 键名,配置行数翻倍,小项目显得啰嗦;
- 新手容易写错缩进,JSON 校验失败时 Grunt 报错信息不友好。
expand 写法
优点:
- 运行时动态展开,可基于 cwd+rename 函数把 src/foo/bar.js 直接映射到 dist/bar.min.js,零临时目录;
- 支持 ext、extDot、flatten 等批量重命名规则,一键处理“哈希+CDN”上线需求;
- 与 grunt-contrib-watch 联动时,增量编译速度最快,杭州电商大促项目实测 2 万图片压缩任务从 90s 降到 12s。
缺点: - rename 函数里一旦用正则替换路径,调试困难,console 打印需手动 grunt.log.debug;
- 展开结果在 Grunt 启动阶段不可见,--verbose 日志爆炸,定位“多匹配”冲突耗时;
- 不适合文件数量极少的场景,配置心智负担高于收益。
一句话总结:
小工具脚本选 compact,多人维护选 files 数组,复杂目录规则+性能敏感选 expand;在国内 CI 环境下,务必加 nonull: true 并配合 grunt-if 做“零文件”熔断,避免“假绿”上线事故。
拓展思考
-
混合策略:
在企业级中台项目中,常用“expand+rename 输出映射表,再被 files 数组消费”两段式:- 第一段 expand 把 packages//src 映射成 dist//lib,同时生成 .grunt/mapping.json;
- 第二段普通 files 任务读取 mapping.json,按模块维度做差异化压缩、sourcemap 上传;
这样既保留 expand 的灵活,又保留 files 的可审查性,美团金融、拼多多供应链系统均用此套路。
-
迁移到 Vite/Webpack:
利用 expand 的 rename 函数,直接把输入输出路径改造成 Webpack entry 对象,通过 grunt-webpack 桥接,实现“Grunt 任务渐进式下线”,B 站直播 PC 端 2022 年用 3 个 Sprint 完成无感知迁移。 -
性能陷阱:
国内很多项目把 expand 的 cwd 指向 node_modules 做“依赖内图片合并”,结果 glob 遍历 20 万文件导致 Grunt 启动 30s+;正确做法是先用 grunt-contrib-clean 删除无关目录,或在 src 数组里加 '!/node_modules/' 负向通配,把启动时间压到 3s 以内。