按任务拆分 Gruntfile 配置文件

使用 Grunt 作为前端构建工具有一段时间了,很感谢 Grunt 开发团队和众多的插件贡献者,得益于 Grunt 及众多的插件,我的前端开发工作效率提升了很多。

痛点

不过,项目目录下的 Gruntfile.js 文件却是越来越大。有的时候为某个功能的开发配置了一个简单的文件合并的任务,可是由于 Grunt 执行时要加载整个 Gruntfile.js 文件,执行内部配置的所有的插件加载、任务注册的流程,结果花费了不少额外的时间。相信随着工作的进一步开展,Gruntfile.js 里面引入的插件也会进一步增加,配置的任务也会越来越多。且不说执行任务的时间,仅仅是查看并修改某个任务的配置也会复杂起来。

可以先看看下面“功能实现”小节的例子中的目录结构和 Gruntfile 文件内容,感受下我的痛苦。

需求

如果可以把 Gruntfile.js 拆分一下就好了。

需求分析

拆分成什么样呢?

由于这个问题还要自己解决,分析的过程其实也是我考虑如何实现的过程。

一开始想,Gruntfile.js 其实是在 Node 中执行的代码,所以可以在其内部搞点小手段,根据执行 grunt 命令时额外传入的参数来加载不同的 外部任务配置文件,外部任务配置文件中是特定模块或功能相关的构建任务。这样以来,单个任务配置文件中只注册需要的插件、任务,文件也会比较简单,运行起来也就比较快了。

能否实现?

那这个通过 Gruntfile.js 再进一步加载特定的外部任务配置文件的流程该如何实现呢?

再看看 grunt 的源码吧,看下能否从中找些灵感。

一看不要紧,原来 grunt 项目中也有这种类似的需求,不过实现方式是这样的:

按任务拆分 Gruntfile 配置文件_第1张图片
Paste_Image.png

源码地址:
https://github.com/gruntjs/grunt/blob/master/internal-tasks/subgrunt.js#L19

看了这个我想了想,通过一个 Gruntfile.js 作为入口,再进一步加载其他的配置文件,弱爆了。不需要这么麻烦,直接写个脚本来根据传入参数调用不同的 Gruntfile 不就是了!

拆分出的任务配置文件的路径定位问题

虽然可以把任务写到拆分的配置文件中,可是还是想将项目的根目录作为配置文件中路径定位的基准。不然,一方面现有的任务配置如果拆分到某个子目录下的配置文件中,任务配置中的文件路径由于是相对路径,还得随之修改,另一方面,新建任务的时候,也要考虑路径问题,并且执行任务命令所在的路径跟任务配置文件中的路径基准不一样,好麻烦....(是的,我描述得就很麻烦)。

然而,grunt 在运行时,不仅可以指定 Gruntfile 的位置(--gruntfile 参数),还可以指定路径的基准目录(--base 参数)。

所以,尽管将任务配置文件拆分到了子目录中,甚至为了组织方便,有几层目录,但执行任务时,仍然可以将项目根目录作为解析相对路径的基准位置。

功能实现

啰嗦那么多,对于程序员而言,倒不如直接上代码来得简洁明了。所以,我还是画个图吧....

原有项目目录:

- src/
  - moduleA/
    - main.js
    - main.less
  - moduleB/
- dist/
  - moduleA/
    - main.bundle.js
    - main.css
  - moduleB/
- node_modules
- Gruntfile.js
- package.json

原有 Gruntfile.js:

module.exports = function (grunt) {
  grunt.initConfig({
    browserify: {
      mod_a: {
        src: 'src/moduleA/main.js',
        dest: 'dist/moduleA/main.bundle.js'
      }
    },
    uglify: {
      mod_b: {
        src: 'src/moduleB/main.js',
        dest: 'dist/moduleB/main.min.js'
      }
    },
    less: {
      mod_a: {
        src: 'src/moduleA/main.less',
        dest: 'dist/moduleA/main.css'
      },
      mod_b: {
        src: 'src/moduleB/main.less',
        dest: 'dist/moduleB/main.css'
      }
    }
  })

  grunt.loadNpmTasks('grunt-contrib-uglify')
  grunt.loadNpmTasks('grunt-contrib-less')
  grunt.loadNpmTasks('grunt-browserify')

  grunt.registerTask('mod_a', ['browserify:mod_a', 'less:mod_a'])
  grunt.registerTask('mod_b', ['uglify:mod_a', 'less:mod_b'])
}

当然,上面只是一个示例,假设有两个模块需要进行构建,不过已经可以看到配置文件都这么长了,而且两个模块尽管不相关,彼此的任务配置也要写到一起。

成果:xsm-cli。

按照 xsm-cli 工具的设计(对,就是我设计的),可以把上面的配置文件按任务进行拆分,拆分后的目录结构:

- src/
  - moduleA/
    - main.js
    - main.less
  - moduleB/
- dist/
  - moduleA/
    - main.bundle.js
    - main.css
  - moduleB/
- node_modules
- *grunt
  - *moduleA/Gruntfile.js
  - *moduleB/Gruntfile.js
- *xsmfile.js
- package.json

注意,上面目录结构中与原有结构不同的地方我用 * 标记了一下。然后来分别看下相应的内容。

moduleA/Gruntfile.js:

module.exports = function (grunt) {
  grunt.initConfig({
    browserify: {
      mod_a: {
        src: 'src/moduleA/main.js',
        dest: 'dist/moduleA/main.bundle.js'
      }
    },
    less: {
      mod_a: {
        src: 'src/moduleA/main.less',
        dest: 'dist/moduleA/main.css'
      }
    }
  })

  grunt.loadNpmTasks('grunt-contrib-less')
  grunt.loadNpmTasks('grunt-browserify')

  grunt.registerTask('default', ['browserify:mod_a', 'less:mod_a'])
}

moduleB/Gruntfile.js:

module.exports = function (grunt) {
  grunt.initConfig({
    uglify: {
      mod_b: {
        src: 'src/moduleB/main.js',
        dest: 'dist/moduleB/main.min.js'
      }
    },
    less: {
      mod_b: {
        src: 'src/moduleB/main.less',
        dest: 'dist/moduleB/main.css'
      }
    }
  })

  grunt.loadNpmTasks('grunt-contrib-uglify')
  grunt.loadNpmTasks('grunt-contrib-less')

  grunt.registerTask('default', ['uglify:mod_a', 'less:mod_b'])
}

看上面两个配置文件,现在是不是清爽得多了,只包含模块相关的任务。

再来看下 xsmfiles.js 是如何配置的:

module.exports = {
  'mod-a': 'moduleA/Gruntfile.js',
  'mod-B': 'moduleB/Gruntfile.js'
}

也很简单吧,就是映射了一下拆分后的任务配置文件。

怎么用呢?

首先得安装下 xsm-cli 这个工具(grunt-cli 以及 grunt 和相关插件在拆分前就应该是已经安装了,不再赘述):

npm install xsm-cli -g

在项目目录下执行命令,如果想执行 mod-a 任务:

xsm mod-a

OK!

其实每个拆分出的任务配置文件,就是一个完整的 Gruntfile 配置文件,所以,也可以执行其中的单个任务,例如:

xsm mod-a less

这样就只执行了模块 A 的 less 编译的任务。

具体实现的代码也没有多少行,感兴趣请阅读源码吧:
https://github.com/xsm-ue/xsm-cli/blob/master/bin/xsm

小结

以上就是我解决工作中使用 Grunt 中任务配置的痛点的过程,写下来做个记录吧。

你可能感兴趣的:(按任务拆分 Gruntfile 配置文件)