按任务拆分 Gruntfile 配置文件

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

痛点

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

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

需求

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

需求分析

拆分成什么样呢?

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

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

能否实现?

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

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

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

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 中任务配置的痛点的过程,写下来做个记录吧。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容

  • 快速入门 Grunt和 Grunt 插件是通过 npm 安装并管理的,npm是 Node.js 的包管理器。Gru...
  • 快速入门 Grunt和 Grunt 插件是通过 npm 安装并管理的,npm是 Node.js 的包管理器。 在安...
    angelwgh阅读 1,096评论 0 0
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,579评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,373评论 25 707
  • 1. distutils distutils是 python 标准库的一部分,2000年发布。使用它能够进行 py...
    杨圣明阅读 320评论 0 1