之前博客中大致描述过“前端自动化构建工具Grunt”及“grunt[mismatched:define]”等信息。然而,并没有深入;下述内容,将深入剖析Grunt Files处理方式、配置项、自定义插件。
一、准备工作
A. 通过npm init
在项目根目录下生成package.json
; B. 通过npm install grunt --save-dev
安装grunt依赖; C. 项目根目录下手动创建文件夹Gruntfile.js
Gruntfile由以下几部分构成: ① “wrapper” 函数 ② 项目与任务配置 ③ 加载grunt插件和任务 ④ 自定义任务
1. Wrapper函数
每一个Gruntfile文件遵循同样的格式,所有代码必须包裹在下述函数中:
module.exports = function(grunt){ // grunt相关其他信息};
2. 项目与任务配置
Grunt的task配置都是在 Gruntfile 中的grunt.initConfig
方法中指定的。
grunt.initConfig({ copy: {}});
3. 加载Grunt插件和任务
只要在 package.json
文件中被列为dependency(依赖)的包,并通过npm install
安装之后,都可以在Gruntfile中以简单命令的形式使用:
// 加载能够提供"copy"任务的插件。grunt.loadNpmTasks('grunt-contrib-copy');
可以通过“load-grunt-tasks”进行一次性载入所有插件,无需依依引入。
require('load-grunt-tasks')(grunt);
4. 自定义任务
(1)自定义任务
grunt.registerTask(taskName, [description, ] taskList)grunt.registerMultiTask(taskName, [description, ] taskFunction)
示例:
grunt.registerTask('cunstom-copy', ['copy:js']);grunt.registerMultiTask('default', ['clean', 'copy']);
注意:但是其自定义name(“cunstom-copy”)不能为上述“原子”任务名称,否则会造成死循环!导致“Maximum call stack size exceeded”问题! (2)基本任务 执行指定的任务函数,并传递任何使用冒号分割的参数作为函数的参数。
grunt.registerTask(taskName, [description, ] taskFunction)
示例:
grunt.registerTask('foo', 'A sample task that logs stuff.', function(arg1, arg2) { grunt.log.writeln(arg1 + "," + arg2); // 内部通过run执行其他任务 grunt.task.run('bar', 'baz');});
二、配置任务
grunt.initConfig
方法中指定任务配置,主要是以任务名称命名的属性,也可以包含其他任意数据。在grunt.initConfig({})
中不针对任何Task的属性,Grunt会将作为常量进行储存,以备后续通过<%= %>
引用。
module.exports = function(grunt){ var config = { app: 'app', // 源文件目录 dest: 'dest' // 目标文件目录 }; grunt.initConfig({ config: config, pkg: grunt.file.readJSON('package.json'), copy: { js: { expand: true, cwd: '<%= config.app %>/', src: ['js/**/*.js'], dest: '<%= config.dest %>/dest/' } } }); // 加载插件 grunt.loadNpmTasks('grunt-contrib-copy')};
config和pkg为常量,其中config在文件开头处定义方便统一,便于后续修改。copy为Task,加载“grunt-contrib-copy”插件copy中的js为Target,可以通过grunt copy:js执行
通配符说明:
A. foo/*.js
将匹配位于foo/目录下的所有的.js
结尾的文件; B. foo/**/*js
将匹配foo/目录以及其子目录中所有以.js
结尾的文件。
模板说明: 使用<%= %>
分隔符指定的模板会在任务从它们的配置中读取相应的数据时将自动扩展扫描。在模板中使用grunt以及它的方法都是有效的,例如: <%= grunt.template.today('yyyy-mm-dd') %>
1. 任务配置和目标
当运行一个任务时,Grunt会自动查找配置对象中的同名属性。 grunt copy
会自动匹配initConfig()
方法中的copy属性,在不指定target情况下,默认将遍历所有目标(target)并依次处理。也可以同时指定任务(task)和目标(target),grunt copy:js
将只会处理指定目标(target)的配置。
2. options属性
在一个任务配置中,options属性可以用来指定覆盖内置属性的默认值。此外,每一个目标(target)中还可以拥有一个专门针对此目标(target)的options属性。注意,options对象是可选的,如果不需要,可以忽略。
grunt.initConfig({ concat: { options: { // 这里是任务级的Options,覆盖默认值 }, foo: { options: { // "foo" target options may go here, overriding task-level options. }, }, bar: { // No options specified; this target will use task-level options. }, },});
3. 文件
由于大多的任务都是执行文件操作,Grunt有一个强大的抽象层用于声明任务应该操作哪些文件。 (1)简洁格式 每个目标对应一个src-dest文件映射
copy: { js: { src: ['<%= config.app %>/js/**/*.js'], dest: '<%= config.dest %>/' }, html: { src: '<%= config.app %>/html/**/*.html', dest: '<%= config.dest %>/' }}
(2)文件对象格式 每个目标对应多个src-dest形式的文件映射,属性名就是目标文件,源文件就是它的值(源文件列表则使用数组格式声明)。
copy: { '<%= config.dest %>/': ['<%= config.app %>/js/**/*.js', '<%= config.app %>/html/**/*.html']}
(3)文件数组格式 每个目标对应多个src-dest文件映射
copy: { bulid: { files: [{ src: ['<%= config.app %>/js/**/*.js'], dest: '<%= config.dest %>/'}, { src: '<%= config.app %>/html/**/*.html', dest: '<%= config.dest %>/'}] }}
“简洁格式”和“文件数组格式”可以支持下列属性:
示例:filter
clean: { foo: { src: ['tmp/**/*'], filter: 'isFile', }, bar: { src: ['tmp/**/*'], filter: function(filepath) { return (grunt.file.isDir(filepath) && require('fs').readdirSync(filepath).length === 0); }, }}
其中isFile为fs.Stats中的方法,可以直接引用!当然,也可以自定义filter,其函数第一个参数为当前文件路径,上述自定义函数为清空空文件夹。
当你希望处理大量的单个文件时,可以通过一些附加的属性来动态的构建一个文件列表。前提,expand 设置为true:
三、补充:1. grunt支持异步等写法,请参考其API2. 创建问题,请参考其常见问题3. 可以通过插件“time-grunt”进行统计每个Task执行时间
require('time-grunt')(grunt);
人若软弱就是自己最大的敌人