Grunt
Grunt 的基本使用
1. 初始化项目
mkdir 02-grunt-sample
cd ./02-grunt-sample
yarn init
2. 添加 grunt 模块
yarn add grunt
3. 添加 gruntfile.js 文件
code gruntfile.js
项目根目录下添加 gruntfile.js 文件。
4. 编写 gruntfile.js 文件
a. 注册任务:
// Grunt 的入口文件
// 用于定义一些需要 Grunt 自动执行的任务
// 需要导出一个函数
// 此函数接收一个 grunt 的形参,内部提供一些创建任务时可以用到的 API
module.exports = grunt => {
grunt.registerTask('demo', () => {
console.log('Task demo: hello grunt.')
})
}
registerTask:第一个参数指定任务名称;第二个参数可以指定任务函数,即任务发生时主动执行的函数;
运行:
yarn grunt demo
yarn:会自动到 node_module 寻找我们需要的插件指令;
grunt:指明了我们使用的插件名称;
demo:指明了要执行的任务名称;
b. 添加多任务,添加任务描述:
// Grunt 的入口文件
// 用于定义一些需要 Grunt 自动执行的任务
// 需要导出一个函数
// 此函数接收一个 grunt 的形参,内部提供一些创建任务时可以用到的 API
module.exports = grunt => {
grunt.registerTask('demo', () => {
console.log('Task demo: hello grunt.')
})
grunt.registerTask('second', '任务描述', () => {
console.log('Task second: hello second.')
})
}
registerTask: 当第二个参数是一个字符串是,该字符串为该任务的一个描述;
查看:
yarn grunt --help
c. default 任务:
// Grunt 的入口文件
// 用于定义一些需要 Grunt 自动执行的任务
// 需要导出一个函数
// 此函数接收一个 grunt 的形参,内部提供一些创建任务时可以用到的 API
module.exports = grunt => {
grunt.registerTask('demo', () => {
console.log('Task demo: hello grunt.')
})
grunt.registerTask('second', '任务描述', () => {
console.log('Task second: hello second.')
})
grunt.registerTask('default', () => {
console.log('Task default: hello default.')
})
}
运行:
yarn grunt
注册任务的时,如果名称设置为 default,那么此任务会成为 grunt 的默认任务。且执行此任务时,无须指定该任务的名称。根据此特性,default 任务一般用于映射其他的任务。
d. default 任务映射其他任务:
// Grunt 的入口文件
// 用于定义一些需要 Grunt 自动执行的任务
// 需要导出一个函数
// 此函数接收一个 grunt 的形参,内部提供一些创建任务时可以用到的 API
module.exports = grunt => {
grunt.registerTask('demo', () => {
console.log('Task demo: hello grunt.')
})
grunt.registerTask('second', '任务描述', () => {
console.log('Task second: hello second.')
})
// grunt.registerTask('default', () => {
// console.log('Task default: hello default.')
// })
grunt.registerTask('default', ['demo', 'second'])
}
运行:
e. grunt 对异步任务的支持:
// Grunt 的入口文件
// 用于定义一些需要 Grunt 自动执行的任务
// 需要导出一个函数
// 此函数接收一个 grunt 的形参,内部提供一些创建任务时可以用到的 API
module.exports = grunt => {
grunt.registerTask('demo', () => {
console.log('Task demo: hello grunt.')
})
grunt.registerTask('second', '任务描述', () => {
console.log('Task second: hello second.')
})
// grunt.registerTask('default', () => {
// console.log('Task default: hello default.')
// })
grunt.registerTask('default', ['demo', 'second'])
grunt.registerTask('async', () => {
setTimeout(() => {
console.log('Task async: async task working...')
}, 1000);
})
}
运行:
我们发现,setTimeout 的任务并没有直接执行。这其实是 grunt 的一个特点:
grunt 默认支持同步模式,如须异步操作,必须使用 this.async() 方法得到回调函数,在异步操作完成后调用此函数以标识此任务已经完成。
改进:
// Grunt 的入口文件
// 用于定义一些需要 Grunt 自动执行的任务
// 需要导出一个函数
// 此函数接收一个 grunt 的形参,内部提供一些创建任务时可以用到的 API
module.exports = grunt => {
grunt.registerTask('demo', () => {
console.log('Task demo: hello grunt.')
})
grunt.registerTask('second', '任务描述', () => {
console.log('Task second: hello second.')
})
// grunt.registerTask('default', () => {
// console.log('Task default: hello default.')
// })
grunt.registerTask('default', ['demo', 'second'])
// grunt.registerTask('async', () => {
// setTimeout(() => {
// console.log('Task async: async task working...')
// }, 1000);
// })
grunt.registerTask('async', function() {
const done = this.async()
setTimeout(() => {
console.log('Task async: async task working...')
done()
}, 1000);
})
}
Grunt 标记任务失败
1. 注册任务:
module.exports = grunt => {
grunt.registerTask('first', () => {
console.log("Task first: hello first");
})
grunt.registerTask('second', () => {
console.log("Task second: this is a failed task.")
return false
})
grunt.registerTask('third', () => {
console.log("Task third: hello third.");
})
}
在 first 任务中,可以通过 return false 来标记当前任务失败;
运行:
yarn grunt first
2. 任务列表中有失败任务,会中断其后续任务的执行:
module.exports = grunt => {
grunt.registerTask('first', () => {
console.log("Task first: hello first");
})
grunt.registerTask('second', () => {
console.log("Task second: this is a failed task.")
return false
})
grunt.registerTask('third', () => {
console.log("Task third: hello third.");
})
grunt.registerTask('default', ['first', 'second', 'third'])
}
运行:
yarn grunt
在 default 任务当中,second 是已知的失败任务,因此 third 任务的执行会被中断。
3. 使用 --force 参数强制执行所有任务:
yarn grunt --force
4. 异步任务标记失败:
module.exports = grunt => {
grunt.registerTask('first', () => {
console.log("Task first: hello first");
})
grunt.registerTask('second', () => {
console.log("Task second: this is a failed task.")
return false
})
grunt.registerTask('third', () => {
console.log("Task third: hello third.");
})
grunt.registerTask('default', ['first', 'second', 'third'])
grunt.registerTask('async', function() {
const done = this.async()
setTimeout(() => {
console.log('Task async: async task working...')
done(false)
}, 1000);
})
}
由上可见,我们可以通过给异步回调函数 this.async() 传入 false 参数,实现异步任务标记失败。
运行:
yarn grunt async
Grunt 的配置方法
1. Grunt 任务添加配置选项:
module.exports = grunt => {
grunt.initConfig({
first: {
path: "../03-grunt-failed-sample/gruntfile.js"
}
})
grunt.registerTask('first', () => {
let firstConfig = grunt.config('first')
console.log("firstConfig: ", firstConfig)
let path = grunt.config('first.path')
console.log("path: ", path)
})
}
initConfig():可通过该方法为任务添加配置选项。该方法接收一个对象作为参数,对象的键一般是任务的名称,值可以任意数据类型。
2. initConfig() 运行使用:
yarn grunt first
Grunt 多目标任务
1. 多目标任务的注册:
module.exports = grunt => {
// 多目标模式,可以让任务根据配置形成多个子任务
grunt.registerMultiTask('build', () => {
console.log('build task')
})
}
registerMultiTask():可通过该方法注册多目标任务。
运行:
此时运行任务会报错。因为运行多目标任务时,我们需要为多目标任务配置不同的目标,配置方式是 —— grunt.initConfig():
module.exports = grunt => {
grunt.initConfig({
build: {
css: 'style.css',
js: 'index.js'
}
})
// 多目标模式,可以让任务根据配置形成多个子任务
grunt.registerMultiTask('build', function() {
console.log('build task')
})
}
配置时,一般使用任务的名称(build)作为配置的名称,然后配置的每一个属性名称就是我们的目标名称。
运行:
yarn grunt build
当直接运行该多目标任务(build)时,会同时运行两个子任务,实际上 grunt 当中称为多目标,即当前任务(build)中有两个目标:css 目标、js 目标。
2. 运行指定目标:
yarn grunt build:css
运行命令是:yarn grunt 任务名称:目标名称
3. 获取目标信息:
module.exports = grunt => {
grunt.initConfig({
build: {
css: 'style.css',
js: 'index.js'
}
})
// 多目标模式,可以让任务根据配置形成多个子任务
grunt.registerMultiTask('build', function() {
console.log('build task')
console.log(`target: ${this.target}, data: ${this.data}`)
})
}
当运行多目标任务时,我们可以通过 this.target 获取目标名称,通过 this.data 获取目标配置数据;
运行:
yarn grunt build
yarn grunt build:css
4. 多目标任务中的 options 关键字:
我们在多目标任务中指定的每一个属性都会成为该任务的一个目标,直接运行未指定目标的多目标任务时,所有的任务都会被执行。但是以 options 关键字命名的属性,不会成为该任务的目标,而是会被作为该任务的配置选项。
module.exports = grunt => {
grunt.initConfig({
build: {
options: {
path: "../04-grunt-config-sample/gruntfile.js",
},
css: 'style.css',
js: 'index.js'
}
})
// 多目标模式,可以让任务根据配置形成多个子任务
grunt.registerMultiTask('build', function() {
console.log('build task')
console.log(`target: ${this.target}, data: ${this.data}`)
})
}
运行:
yarn grunt build
可以看到 options 并未被执行。
5. 获取多目标任务的配置选项:
我们可以通过 this.options() 来获取多目标任务的配置选项。
module.exports = grunt => {
grunt.initConfig({
build: {
options: {
path: "../04-grunt-config-sample/gruntfile.js",
},
css: 'style.css',
js: 'index.js'
}
})
// 多目标模式,可以让任务根据配置形成多个子任务
grunt.registerMultiTask('build', function() {
console.log('build task')
console.log(`target: ${this.target}, data: ${this.data}`)
console.log("options: ", this.options())
})
}
运行:
yarn grunt build
6. options 配置选项的覆盖:
如果多目标任务的某个目标也定义了 options,那么在运行此目标时,目标中的 options 会覆盖任务中的 options。
module.exports = grunt => {
grunt.initConfig({
build: {
options: {
path: "../04-grunt-config-sample/gruntfile.js",
},
css: {
options: {
path: "./",
},
file: 'style.css'
},
js: 'index.js'
}
})
// 多目标模式,可以让任务根据配置形成多个子任务
grunt.registerMultiTask('build', function() {
console.log('build task')
console.log(`target: ${this.target}, data: ${this.data}`)
console.log("options: ", this.options())
})
}
运行:
可以看到,css 目标中的 options 覆盖了任务中的 options,但是 js 目标中由于未定义 options,所以 js 目标中获取到的依旧是任务中的 options。
Grunt 插件的使用
插件的使用意义:由于很多的构建任务都是通用的(例如:压缩代码)。因此,社区当中就出现了很多预设的插件,这些插件内部会封装一些通用的构建任务,而我们的构建过程正是由这些通用的构建任务组成的。
插件的使用方法:
graph TD
npm安装插件 --> gruntfile.js载入插件提供的任务 --> 根据插件的文档完成相关的配置选项
插件 grunt-contrib-clean 的使用:
grunt-contrib-clean: 用于自动清除项目开发过程当中产生的临时文件。
1. 安装插件
yarn add grunt-contrib-clean
2. 载入插件
module.exports = grunt => {
grunt.loadNpmTasks('grunt-contrib-clean')
}
3. 插件使用
yarn grunt clean
从运行结果提示 clean 任务未配置对应的目标。由此可以知道 clean 任务是一种多目标任务,需要通过 initConfig() 方法去配置不同的目标。
4. 添加任务的配置选项
a. 直接指定文件路径:
module.exports = grunt => {
grunt.initConfig({
clean: {
// 键为目标名称,值为该目标所要清除的文件路径
temp: 'tmp/a.js'
}
})
grunt.loadNpmTasks('grunt-contrib-clean')
}
yarn grunt clean:temp
b. 通配符方式指定文件路径:
module.exports = grunt => {
grunt.initConfig({
clean: {
// 键为目标名称,值为该目标所要清除的文件路径
temp: 'tmp/*.txt'
}
})
grunt.loadNpmTasks('grunt-contrib-clean')
}
yarn grunt clean:temp
清除 tmp 目录下,所有后缀为 .txt 的文件。
module.exports = grunt => {
grunt.initConfig({
clean: {
// 键为目标名称,值为该目标所要清除的文件路径
temp: 'tmp/**'
}
})
grunt.loadNpmTasks('grunt-contrib-clean')
}
yarn grunt clean:temp
清除 tmp 目录下所有子目录以及子目录下的所有文件。
实现常用的构建任务(grunt-sass,grunt-babel):
1. 项目初始化:
mkdir 07-grunt-demo
cd ./07-grunt-demo
yarn init
yarn add grunt
code gruntfile.js
yarn add grunt-sass sass --dev
2. 载入任务(grunt-sass):
module.exports = grunt => {
grunt.initConfig({
})
grunt.loadNpmTasks('grunt-sass')
}
3. 配置目标:
module.exports = grunt => {
grunt.initConfig({
sass: {
main: {
files: {
'dist/css/main.scss': 'src/scss/main.scss', // 输出路径:源路径
}
}
}
})
grunt.loadNpmTasks('grunt-sass')
}
4. 运行 sass 任务:
yarn grunt sass
由运行结果提示可知:缺少了 implementation 选项,该选项用于指定 grunt-sass 当中使用哪个模块对 sass 文件进行编译,因此我们需要为当前的 sass 任务添加一个配置选项(options).
5. 调整(为任务增加 implementation 选项):
const sass = require('sass')
module.exports = grunt => {
grunt.initConfig({
sass: {
options: {
implementation: sass
},
main: {
files: {
'dist/css/main.css': 'src/scss/main.scss'
}
}
}
})
grunt.loadNpmTasks('grunt-sass')
}
运行:
可以看到 dist/css/main.css 已经被生成。
6. 为 sass 任务添加其他配置选项(sourceMap):
const sass = require('sass')
module.exports = grunt => {
grunt.initConfig({
sass: {
options: {
sourceMap: true,
implementation: sass
},
main: {
files: {
'dist/css/main.css': 'src/scss/main.scss'
}
}
}
})
grunt.loadNpmTasks('grunt-sass')
}
运行:
7. 安装 grunt-babel 插件:
yarn add grunt-babel @babel/core @babel/preset-env --dev
8. 安装 load-grunt-tasks 插件:
yarn add load-grunt-tasks --dev
load-grunt-task:可以减少 loadNpmTasks 方法的使用,可以自动加载所有的 grunt 插件中的任务;
9. 配置 babel 转换的 preset 选项:
const sass = require('sass')
const loadGruntTasks = require('load-grunt-tasks')
module.exports = grunt => {
grunt.initConfig({
sass: {
options: {
sourceMap: true,
implementation: sass
},
main: {
files: {
'dist/css/main.css': 'src/scss/main.scss'
}
}
},
babel: {
options: {
presets: ['@babel/preset-env'] // @babel/preset-env: es 语法的编译
},
main: {
files: {
'dist/js/a.js': 'src/js/a.js'
}
}
}
})
// grunt.loadNpmTasks('grunt-sass')
loadGruntTasks(grunt) // 自动加载所有的 grunt 插件中的任务
}
- @babel/preset-env:es语法编译
- @babel/preset-flow:编译js代码静态类型检查器flow
- @babel/preset-react:编译react语法
- @babel/preset-typescript:编译ts语法
10. 运行 babel 任务:
yarn grunt babel
11. 为 babel 任务添加其他配置选项(sourceMap):
const sass = require('sass')
const loadGruntTasks = require('load-grunt-tasks')
module.exports = grunt => {
grunt.initConfig({
sass: {
options: {
sourceMap: true,
implementation: sass
},
main: {
files: {
'dist/css/main.css': 'src/scss/main.scss'
}
}
},
babel: {
options: {
sourceMap: true,
presets: ['@babel/preset-env'] // @babel/preset-env: es 语法的编译
},
main: {
files: {
'dist/js/a.js': 'src/js/a.js'
}
}
}
})
// grunt.loadNpmTasks('grunt-sass')
loadGruntTasks(grunt) // 自动加载所有的 grunt 插件中的任务
}
12. 监测文件修改后自动编译(grunt-contrib-watch):
yarn add grunt-contrib-watch --dev
13. 添加 grunt-contrib-watch 的任务:
const sass = require('sass')
const loadGruntTasks = require('load-grunt-tasks')
module.exports = grunt => {
grunt.initConfig({
sass: {
options: {
sourceMap: true,
implementation: sass
},
main: {
files: {
'dist/css/main.css': 'src/scss/main.scss'
}
}
},
babel: {
options: {
sourceMap: true,
presets: ['@babel/preset-env'] // @babel/preset-env: es 语法的编译
},
main: {
files: {
'dist/js/a.js': 'src/js/a.js'
}
}
},
watch: {
js: {
// 监听 js 文件的变化
files: ['src/js/*.js'],
tasks: ['babel'] // 当 files 当中的文件发生修改时,需要执行的任务列表
},
css: {
files: ['src/scss/*.scss'],
tasks: ['sass']
}
}
})
// grunt.loadNpmTasks('grunt-sass')
loadGruntTasks(grunt) // 自动加载所有的 grunt 插件中的任务
}
14. 运行 watch 任务:
yarn grunt watch
运行 watch 任务后,并不会直接执行 babel 任务或者 sass 任务,它只会监听自己目标对应的文件,一但文件发生了变化,它才会执行响应的任务;
15. 将 watch 任务映射到 default 任务中:
const sass = require('sass')
const loadGruntTasks = require('load-grunt-tasks')
module.exports = grunt => {
grunt.initConfig({
sass: {
options: {
sourceMap: true,
implementation: sass
},
main: {
files: {
'dist/css/main.css': 'src/scss/main.scss'
}
}
},
babel: {
options: {
sourceMap: true,
presets: ['@babel/preset-env'] // @babel/preset-env: es 语法的编译
},
main: {
files: {
'dist/js/a.js': 'src/js/a.js'
}
}
},
watch: {
js: {
// 监听 js 文件的变化
files: ['src/js/*.js'],
tasks: ['babel'] // 当 files 当中的文件发生修改时,需要执行的任务列表
},
css: {
files: ['src/scss/*.scss'],
tasks: ['sass']
}
}
})
// grunt.loadNpmTasks('grunt-sass')
loadGruntTasks(grunt) // 自动加载所有的 grunt 插件中的任务
grunt.registerTask('default', ['sass', 'babel', 'watch'])
}
此时,sass 任务和 babel 任务会先被编译一次以后,再执行 watch 任务,以监听后续文件的变化再进行自动编译。