解释 cwd、src、dest、ext 在文件模板中的执行顺序
解读
在 Grunt 的 files 数组配置里,每一条映射规则都会经历“路径解析 → 文件匹配 → 目标生成 → 后缀修正”四步流水线。
面试官想确认的是:
- 你能否把四元参数(cwd、src、dest、ext)放进同一条时间线;
- 你是否理解每一步产生的中间结果,以及后一步如何消费前一步的输出;
- 当四元参数同时出现时,哪一步才真正决定“文件落盘位置”和“最终后缀”。
国内项目普遍混合 ES5/TS、图片、字体等多类型资源,一旦顺序理解错误,就会出现“文件找不到”“输出层级多一层”“压缩后后缀仍是 .js”等线上事故,所以该考点在简历面与技术面都会被追问。
知识点
- cwd(current working directory)
仅影响 src 的匹配基准路径,不会拼接到 dest。 - src
支持通配符,匹配结果 = cwd + 通配符展开后的相对路径列表。 - dest
可以是目录或模板字符串;若 dest 以/结尾表示目录,否则视为目标文件路径模板。 - ext
在 dest 模板计算完毕后再做字符串替换,仅修改后缀名,不改变目录层级。
官方源码顺序(grunt-contrib-copy / uglify / sass 均复用同一逻辑):
cwd → src → dest → ext
答案
执行顺序严格为 cwd → src → dest → ext,每一步的输入都是上一步的输出:
- cwd 生效:Grunt 先把进程工作目录切到 cwd 指定的目录,后续所有 src 通配符都在该目录下展开。
- src 展开:根据上一步的基准目录,利用 minimatch 得到“相对 cwd”的文件数组;结果中已去掉 cwd 前缀。
- dest 计算:对每条匹配到的文件,把 src 的相对段拼到 dest 模板后面,形成“目标路径草稿”;如果 dest 里出现
{{filename}}、{{ext}}等占位符,也在这一步完成字符串渲染。 - ext 重写:扫描第 3 步得到的草稿路径,若配置了 ext,则无条件把最后一个
.xxx段替换为 ext 指定的新后缀,生成最终的磁盘路径。
示例:
files: [{
cwd: 'src',
src: '**/*.coffee',
dest: 'dist/js/',
ext: '.js'
}]
- cwd 把搜索基准定在
src/ - src 匹配到
src/a/b.coffee→ 中间结果a/b.coffee - dest 目录模板拼接 →
dist/js/a/b.coffee - ext 替换 → 最终输出
dist/js/a/b.js
拓展思考
- flatten 与 rename 函数
若同时出现flatten: true,则在 dest 计算前先把目录拍平;若提供rename函数,则 ext 替换后还会再走一次自定义映射,顺序变为 cwd → src → flatten → dest → ext → rename。 - 动态映射性能
国内大型项目 10k+ 文件场景下,cwd 若指到项目根,会导致 minimatch 全树扫描;推荐把 cwd 拆到最小公共子目录,可缩短 30% 构建时间。 - 多 ext 规则
当需要把.ts变.js、同时把.scss变.css时,不要写两条任务,而是利用expand: true配合extDot: 'last'让 ext 只替换最后一个点号,避免把component.d.ts误改成component.d.js。 - 与 webpack/vite 差异
面试官常追问“为何 Grunt 需要四元组而 webpack 不需要”。核心差异在于 Grunt 是任务拼装,路径变换由用户显式声明;webpack 是依赖图驱动,路径规则内置于 loader。理解这一点,可以顺势把话题引到“如何渐进式从 Grunt 迁移到 rollup/esbuild”上,展示你对国内老项目改造的深度思考。