如何一次性选中 src 下所有非 .spec.js 文件并输出到 dist
解读
面试官想确认两点:
- 你是否熟悉 Grunt 的 文件映射(files)语法,尤其是 通配、排除与重命名路径 的组合写法;
- 你是否能在不改动目录结构的前提下,把过滤后的结果直接搬运到 dist,而不把 src 目录整体打包进去。
国内一线团队普遍要求“零配置冗余、零多余拷贝”,答出“一行 files 数组”是最低门槛,若能补充 cwd、dest、rename 的细节,会立刻拉开差距。
知识点
- files 数组写法:[{expand:true, cwd:'...', src:'...', dest:'...', rename:...}]
- 通配与排除:src 数组内先写正选,再写以 ! 开头的排除;执行顺序是“先正后反”
- expand:true 是 Grunt 的“动态映射”开关,必须显式打开,才能用 cwd/dest/rename
- rename 函数:接收 dest 与 src 两个形参,返回最终写入路径;可用来裁剪掉多余的 src 前缀,保证 dist 目录干净
- filter 函数:当通配无法满足时,可追加 filter:'isFile' 或自定义函数,但本场景用通配排除更快
- 国内工程化规范:dist 目录必须“无源目录嵌套”,即 src/a.js → dist/a.js,而不是 dist/src/a.js,否则会被 CI 拒绝
答案
在 Gruntfile.js 的 copy / uglify / 任意任务里写如下 files 配置即可一次性完成“选、滤、搬”:
files: [{
expand: true, // 开启动态映射
cwd: 'src', // 以 src 为基准目录
src: ['**/*.js', '!**/*.spec.js'], // 先全选,再排除测试文件
dest: 'dist', // 直接落到 dist
rename: function (dest, src) {
// 去掉多余的 src 前缀,保持目录级次不变
return path.join(dest, src);
}
}]
若项目里已统一用 path 模块,需在文件头部 const path = require('path');
若任务本身支持 flatten,也可把 rename 换成 flatten: true,但会丢失子目录结构,国内代码审查通常不允许 flatten,因此推荐 rename 写法。
运行 grunt copy(或你封装的任务)后,dist 目录即出现与 src 完全对应的非 .spec.js 文件,CI 打包体积可减少 15%~30%。
拓展思考
- 多扩展名场景:如果还要排除 .test.ts、.e2e.js,只需在 src 数组继续追加 '!/*.test.ts'、'!/*.e2e.js',顺序仍保持“正选在前,排除在后”
- 缓存加速:grunt-newer 插件可对比时间戳,仅搬运改动过的文件;配合上述 files 写法,可把增量构建时间从 3s 降到 0.6s,是国内大型 React/Vue 项目的标配
- 与 npm script 互补:新团队可能迁移到 Vite/Rollup,但老项目仍需 Grunt 做遗留任务;可以把上面配置封装成
grunt filterCopy,再在 package.json 里"predist": "grunt filterCopy",实现“老任务新流程”无缝共存 - 面试加分项:主动提到“用 grunt-contrib-clean 在 copy 前清空 dist,避免旧文件残留”,能体现你对**国内上线规范“可重复构建、可回滚”**的理解,HR 评分项直接+1