如何在不安装插件的前提下通过 grunt.task.run 串行执行多函数

解读

面试官抛出“不安装插件”这一限制,目的是考察候选人对 Grunt 任务队列机制task.registerMultiTask / registerTask 的底层理解,而非只会 grunt.loadNpmTasks。国内一线厂普遍要求“最小依赖、最大可控”,尤其在基建组或外包审计场景,禁止随意装包是红线。因此,必须利用 Grunt 原生 API 把若干普通函数封装成具名任务,再借助 grunt.task.run串行调度能力完成流水线。

知识点

  1. 任务即函数grunt.registerTask('foo', function () { ... }) 会把函数注册成任务,函数体内必须返回 true / false 或调用 this.async() 告知 Grunt 结束时机
  2. 串行队列grunt.task.run(['a','b','c']) 会把任务名依次压入当前任务上下文的内部队列,前一个任务“完成标记”打出后,后一个才会出队
  3. 无插件原则:禁止 grunt.loadNpmTasks,但可手写普通函数并通过 registerTask 包装,不依赖任何 npm 包
  4. 错误熔断:任务函数中 return falsegrunt.fail.warn()中断后续任务,符合国内 CI 质量门禁要求。
  5. this.async():当任务内部有异步 IO(如读文件、拉取远端配置)时必须调用,否则 Grunt 会提前认为任务成功

答案

在 Gruntfile.js 中按以下步骤操作,全程零插件

module.exports = function (grunt) {
  // 1. 定义纯函数
  function clean() {
    grunt.log.writeln('>> 清理 dist 目录');
    grunt.file.delete('dist', { force: true });
    return true;          // 同步任务直接返回 true
  }

  function buildJS() {
    const done = this.async(); // 标记异步
    setTimeout(() => {
      grunt.file.write('dist/app.js', 'console.log("hello");');
      grunt.log.ok('JS 构建完成');
      done();                  // 通知 Grunt 本任务结束
    }, 300);
  }

  function copyHtml() {
    grunt.file.copy('src/index.html', 'dist/index.html');
    grunt.log.ok('HTML 拷贝完成');
    return true;
  }

  // 2. 注册成具名任务
  grunt.registerTask('clean', clean);
  grunt.registerTask('buildJS', buildJS);
  grunt.registerTask('copyHtml', copyHtml);

  // 3. 默认任务里串行调度
  grunt.registerTask('default', function () {
    grunt.task.run(['clean', 'buildJS', 'copyHtml']);
    grunt.log.writeln('>> 所有任务已按序进入队列');
  });
};

运行 npx grunt 即可看到clean → buildJS → copyHtml 严格串行,任一环节返回 false 或未调用 done(),后续任务不会执行,满足国内企业对可中断、可追踪、零依赖的强制要求。

拓展思考

  1. 动态任务名:在 grunt.task.run 之前,可根据命令行参数 grunt.option('env') 决定加入哪些任务,实现开发/生产两套流水线而无需额外插件。
  2. 并行优化:虽然题目要求串行,但可把无依赖的子任务拆成多目标(如 cssmin:prodcssmin:lib),再用 grunt.task.run(['concurrent:css']) 做并行,并发目标仍通过原生注册实现,同样不引入第三方。
  3. 结果缓存:在函数内部用 grunt.file.exists('.cache/foo')增量判断,跳过未变更任务,提升大型仓库二次构建速度,该技巧在国内 Monorepo 基建中可节省 30%+ 时间。