1. 基本使用

项目中想要使用grunt的话,首先需要安装他。

yarn add grunt --dev

安装过后需要在项目跟目录添加gruntfile.js文件作为入口文件,用于定义一些需要grunt自动执行的任务。这个文件导出一个函数,函数接收一个grunt参数,参数是一个对象,对象中就是grunt提供的一些api。比如借助registerTask方法注册任务。

这个方法第一个参数指定任务名字,第二个参数指定任务函数。

module.exports = grunt => {
    grunt.registerTask('foo', () => {
        console.log('hello grunt');
    })
}
yarn grunt foo

foo就是注册任务的名字,grunt会自动的执行foo任务,当然也可以添加更多的任务。

如果添加任务的时候第二个参数指定一个字符串,这个字符串就是这个任务的描述,他会出现在grunt的帮助信息中。可以通过grunt --help得到grunt的帮助信息,帮助信息中有一个avaible tasks,在这个tasks当中任务描述就是自定义的任务描述。

module.exports = grunt => {
    grunt.registerTask('foo', () => {
        console.log('hello grunt');
    })
    grunt.registerTask('bar', '任务描述', () => {
        console.log('other task');
    })
}

如果在注册任务的时候任务名称叫做default,那这个任务将会成为grunt的默认任务,在运行任务的时候不需要指定任务的名称,grunt将自动调用default

module.exports = grunt => {
    grunt.registerTask('foo', () => {
        console.log('hello grunt');
    })
    grunt.registerTask('bar', '任务描述', () => {
        console.log('other task');
    })
    grunt.registerTask('default', () => {
        console.log('default task');
    })
}

一般会用default去映射一些其他的任务,一般的做法是在registerTask函数第二个参数传入一个数组,这个数组中可以指定一些任务的名字。

module.exports = grunt => {
    grunt.registerTask('foo', () => {
        console.log('hello grunt');
    })
    grunt.registerTask('bar', '任务描述', () => {
        console.log('other task');
    })
    grunt.registerTask('default', ['foo', 'bar']);
}

这时执行default就会依次执行数组中的任务。

yarn grunt

grunt代码默认支持同步模式,如果需要异步操作必须使用thisasync方法得到一个回调函数,在异步操作完成后调用这个回调函数,标识一下这个任务已经被完成。

module.exports = grunt => {
    grunt.registerTask('foo', () => {
        console.log('hello grunt');
    })
    grunt.registerTask('bar', '任务描述', () => {
        console.log('other task');
    })
    grunt.registerTask('default', ['foo', 'bar']);

    grunt.registerTask('async-task', function () {
        const done = this.async();
        setTimeout(() => {
            console.log('async task');
            done();
        }, 1000)
    })
}
yarn grunt async-task

2. 标记任务失败

如果在构建任务的逻辑代码当中发生错误,例如需要的文件找不到了,此时就可以将这个任务标记为一个失败的任务。具体实现可以通过在函数体当中return false来实现。

module.exports = grunt => {
    grunt.registerTask('bad', () => {
        console.log('bad working~')
        return false
    })
}

如果这个任务是在任务列表当中那这个任务的失败会导致后续任务不在被执行。例如这里有多个任务通过default连在一起,正常情况他们会依次执行,但是当bad失败的时候bar也就不执行了。

module.exports = grunt => {
    grunt.registerTask('bad', () => {
        console.log('bad working~')
        return false
    })
    grunt.registerTask('foo', () => {
        console.log('foo task~')
        return false
    })
    grunt.registerTask('bar', () => {
        console.log('bar task~')
        return false
    })
    grunt.registerTask('default', ['foo', 'bad', 'bar']);
}

运行的时候使用--force可以强制执行所有任务,使用--force之后即使bad任务执行失败了也是会正常去执行bar

yarn grunt default --force

如果任务是一个异步任务就没有办法直接通过return false标记任务失败,需要给异步的回调函数指false实参标记任务失败。

module.exports = grunt => {
    grunt.registerTask('bad-async', function() {
        const done = this.async();
        setTimeout(() => {
            console.log('bad async');
            done(false)
        }, 1000)
    })
}

3. 配置方法

grunt还提供一个用于添加任务选项的API叫做initConfig,例如使用grunt压缩文件时就可以通过这种方式配置压缩的文件路径。

这个方法接收一个对象形式的参数,对象的属性名一般与任务名称保持一致,属性的值他可以是任意类型的数据。有了这个配置属性就可以在任务中使用这个配置属性。

这里注册一个叫做foo的任务,在任务中通过grunt提供的config方法获取这个配置,config方法接收一个字符串参数,这个参数就是initConfig中指定的字符串名字。

module.exports = grunt => {
    grunt.initConfig({
        foo: 'bar'
    })

    grunt.registerTask('foo', () => {
        const result = grunt.config('foo');
        console.log(result);
    })
}

在执行任务的时候就会获取到initConfig的内容。如果foo是对象的话,在config可以通过.的方式获取到属性值。

module.exports = grunt => {
    grunt.initConfig({
        foo: {
            bar: 123
        }
    })

    grunt.registerTask('foo', () => {
        const result = grunt.config('foo.bar');
        console.log(result);
    })
}
grunt foo

4. 多目标任务

多目标形式任务可以理解为子任务的概念,这种形式的任务在通过grunt实现各种构建任务时非常有用。多目标任务需要通过grunt中的registerMultiTask方法定义,这个方法同样接收两个参数,第一个是任务名字,第二个是函数。

module.exports = grunt => {
    grunt.registerMultiTask('build', function() {
        console.log('task');
    })
}
grunt build

使用这种多任务需要配置任务目标,配置方式通过initConfig去配置,需要指定一个与任务名称同名的属性也就是build,并且属性值必须是个对象,对象中每个属性的名字就是目标名称。

这相当于为build任务添加了两个目标,一个是css一个是js。此时在运行的时候会执行两个任务,也就是build任务有两个目标一个js一个css

module.exports = grunt => {
    grund.initConfig({
        build: {
            css: '1',
            js: '2'
        }
    })

    grunt.registerMultiTask('build', function() {
        console.log('task');
    })
}
grunt build

如果需要执行指定目标的时候可以通过build:css

grunt build:css

在这个任务函数中可以通过this.target拿到当前执行的目标名字,还可以通过thisdata拿到这个target对应的数据。

module.exports = grunt => {
    grund.initConfig({
        build: {
            css: '1',
            js: '2'
        }
    })

    grunt.registerMultiTask('build', function() {
        console.log(`${this.target} ${this.data}`);
    })
}

需要注意的是在build中指定的每一个属性的键都会成为一个目标,除了指定的options以外,在options当中指定的信息会作为任务的配置选项出现。可以通过this.options()拿到配置选项。

module.exports = grunt => {
    grund.initConfig({
        build: {
            options: {
                foo: 'bar'
            },
            css: '1',
            js: '2'
        }
    })

    grunt.registerMultiTask('build', function() {
        console.log(`${this.target} ${this.data} ${this.options()}`);
    })
}

除了可以在任务中添加这个选项还可以在目标中添加,如果目标也是一个对象那么这个属性中也可以添加一个options,添加之后会覆盖掉对象中的options

module.exports = grunt => {
    grund.initConfig({
        build: {
            options: {
                foo: 'bar'
            },
            css: {
                options: {
                    foo: 'baz'
                }
            },
            js: '2'
        }
    })

    grunt.registerMultiTask('build', function() {
        console.log(`${this.target} ${this.data} ${this.options()}`);
    })
}

5. 插件

插件是grunt的核心存在的原因也非常简单,因为很多构建任务都是通用的,例如在项目中需要压缩代码,所以社区当中就出现了很多预设插件,那这些插件内部都封装了很多通用的构建任务。一般情况下构建过程都是由这些通用的构建任务组成的。

使用插件的过程非常简单,大体是先通过npm去安装这个插件,再到gruntfile中载入这个插件提供的一些任务,最后根据插件的文档完成配置选项。

这里通过grunt-contrib-clean插件自动清除项目开发过程中产生的一些临时文件。

yarn add grunt-contrib-clean

grunt中通过grunt.loadNpmTasks的方式加载这个插件中提供的一些任务。绝大多数情况下grunt的命名规范都是grunt-contrib-taskname,所以这里的clean插件提供的任务名称应该就叫clean

clean是一种多目标任务,需要使用config的方式配置不同的目标。

module.exports = grunt => {
    grunt.initConfig({
        clean: {
            temp: 'temp/app.js'
        }
    })

    grunt.loadNpmTask('grunt-contrib-clean');
}

运行clean命令的时候就会删除掉temp/app.js文件。除了具体文件还可以使用通配符的方式通配一些文件类型。例如可以删除所有的txt文件,也可以使用**删除temp下所有的文件。

module.exports = grunt => {
    grunt.initConfig({
        clean: {
            temp: 'temp/*.txt'
        }
    })

    grunt.loadNpmTask('grunt-contrib-clean');
}

6. 常用插件

grunt官方提供了一个sass模块,但是那个模块需要本机安装sass环境,使用起来很不方便。这里使用的grunt-sass是一个npm模块,他在内部会通过npm的形式去依赖sass。这在使用起来不需要对机器有任何的环境要求。

使用的方式也是需要先安装他。grunt-sass需要有一个sass模块支持,这里使用的是sass官方提供的npm模块, 把这两个模块安装到开发依赖当中。

yarn add grunt-sass sass --dev

安装之后通过grunt.loadNpmTasks载入grunt-sass中提供的任务。还要在grunt.initConfig中配置多任务目标, main中需要指定sass输入文件。以及最终输出的css文件的路径。可以通过files属性键是输出文件路径,值就是原路径也就是sass文件的路径。

还需要添加一个optionsimplementation模块,指定依赖的模块。

const sass = require('sass');

module.exports = grunt = > {
    grunt.initConfig({
        sass: {
            options: {
                implementation: sass
            },
            main: {
                files: {
                    'dist/css/main.css': 'src/scss/main.scss'
                }
            }
        }
    })

    grunt.loadNumTasks('grunt-sass')
}

开发中常用的插件还有babel,一般通过他把ES6的语法转换为浏览器可识别的ES5, 在grunt中如果想要使用babel可以使用一个叫做grunt-babel的插件。

yarn add grunt-babel @babel/core @babel/preset-env --dev

也是需要使用loadNpmTasks加载grunt-babel的任务。随着任务越来越多loadNpmTasks也会越来越多,这个时候社区有一个模块可以减少loadNpmTasks的使用叫做loadgruntTasks模块。

yarn add load-grunt-tasks --dev

安装之后可以先导入这个模块, 这样就不需要每次重复的导入插件了,可以直接通过loadgruntTasks调用自动加载grunt的所有插件。

const sass = require('sass');
const loadgruntTasks = require('load-grunt-tasks');

module.exports = grunt = > {
    grunt.initConfig({
        sass: {
            options: {
                implementation: sass
            },
            main: {
                files: {
                    'dist/css/main.css': 'src/scss/main.scss'
                }
            }
        }
    })

    loadgruntTasks(grunt);
    // grunt.loadNumTasks('grunt-sass')
}

可以为grunt-babel提供一些任务配置,首先也是配置输入和输出路径。需要为babel设置一个options设置的是babel在转换时候的preset。还可以为js文件生成source-map

const sass = require('sass');
const loadgruntTasks = require('load-grunt-tasks');

module.exports = grunt = > {
    grunt.initConfig({
        sass: {
            options: {
                implementation: sass
            },
            main: {
                files: {
                    'dist/css/main.css': 'src/scss/main.scss'
                }
            }
        },
        babel: {
            options: {
                sourceMap: true,
                presets: ['@babel/preset-env']
            },
            main: {
                files: {
                    'dist/css/app.js': 'src/scss/app.js'
                }
            }
        }
    })

    loadgruntTasks(grunt);
    // grunt.loadNumTasks('grunt-sass')
}

还有一个特性是当文件修改之后自动编译,需要grunt-contrib-watch插件。

yarn add grunt-contrib-watch --dev

安装后loadgruntTasks会自动把这个任务加载进来。watch可以配置不同的目标,例如这里配置一个js目标,files是一个数组,因为他不需要输出目标,只需要设置监听目标就可以了。还需要设置一个tasks,也就是当文件发生改变要执行什么任务, 这里写上babel

const sass = require('sass');
const loadgruntTasks = require('load-grunt-tasks');

module.exports = grunt = > {
    grunt.initConfig({
        watch: {
            js: {
                files: ['src/js/*.js'],
                tasks: ['babel']
            }
        }
    })

    loadgruntTasks(grunt);
    // grunt.loadNumTasks('grunt-sass')
}

这个时候一旦文件发生变化,就会执行babel任务。

一般会给watch做一个映射,是为了确保再启动的时候先执行一下sassbabel任务,最后在执行watch任务。

const sass = require('sass');
const loadgruntTasks = require('load-grunt-tasks');

module.exports = grunt = > {
    grunt.initConfig({
        watch: {
            js: {
                files: ['src/js/*.js'],
                tasks: ['babel']
            }
        }
    })

    loadgruntTasks(grunt);

    grunt.registerTask('default', ['sass', 'babel', 'watch']);
}

这三个小插件实际上是使用grunt工具最常用的插件。grunt已经退出历史舞台了。

转载须知

如转载必须标明文章出处文章名称文章作者,格式如下:

转自:【致前端 - zhiqianduan.com】 grunt打包工具  "隐冬"
请输入评论...