gulp 是基于 node 实现 Web 前端自动化开发的工具。
gulp 还可以做很多事,例如:
- 压缩CSS
- 压缩图片
- 编译Sass/LESS
- 编译CoffeeScript
- markdown 转换为 html
一、安装gulp
1、安装gulp
$ npm install -g gulp //安装全局包
$ npm install -g gulp-cli //安装gulp-cli
2、创建项目目录并进入
$ npx mkdirp my-project //使用mkdirp命令创建my-project目录
$ cd my-project //进入目录
$ npm init -y //在项目目录下 创建 package.json 文件
3、安装 gulp,作为开发时依赖项
$ npm install --save-dev gulp //安装到开发依赖
$ gulp -v //查看版本信息
4、在项目根目录下创建一个名为 gulpfile.js 的文件:
var gulp = require('gulp');
gulp.task('default', function() { // 将你的默认的任务代码放在这
});
二、gulp基本转换流程
1、找到 js/目录下的所有 .js 文件
2、压缩这些 js 文件
3、将压缩后的代码另存在 dist/js/ 目录下
// 压缩 JavaScript 文件
var gulp = require('gulp') // 获取 gulp
var uglify = require('gulp-uglify') // 获取 uglify 模块(用于压缩 JS)
gulp.task('script', function(done) {
gulp.src('js/*.js') // 1. src找到文件
//.pipe是接受上一个流的结果,并返回一个处理后流的结果
.pipe(uglify()) // uglify 模块压缩文件
.pipe(gulp.dest('dist/js')); // dest输出压缩文件
done(); //要求必须有回调函数
});
代码执行后文件结构
└── js/
│ └── a.js
└── dist/
└── js/
└── a.js
- src() 读取文件然后生成一个 Node 流(stream)。它将所有匹配的文件读取到内存中并通过流(stream)进行处理。
- 流(stream)所提供的主要的 API 是 .pipe() 方法。
- dest() 接受一个输出目录作为参数,它会将文件内容及文件属性写入到指定的目录中。
gulp 还提供了 symlink() 方法,类似 dest(),但是创建的是链接而不是文件。
三、特殊字符 * 、* *、!、{}
gulp.src('./js/*.js') // * 匹配js文件夹下所有.js格式的文件
gulp.src('./js/**/*.js') // ** 匹配js文件夹的0个或多个子文件夹
gulp.src(['./js/*.js','!./js/index.js']) // ! 匹配除了index.js之外的所有js文件
gulp.src('./js/**/{omui,common}.js') // {} 匹配{}里的文件名
1、* ,对于匹配单级目录下的文件很有用。
2、* * ,对于匹配嵌套目录下的文件很有用。
3、! 反取,glob 数组中的取反必须跟在一个非取反的后面。
四、任务(task)
每个 gulp 任务(task)都是一个异步的 JavaScript 函数。
此函数是一个可以接收 callback 作为参数的函数,或者是一个返回 stream、promise、event emitter、child process 或 observable 类型值的函数。
1、导出任务
被 gulpfile 导出(export)的任务为公开任务,未被导出的任务被认为是私有任务。
const { series } = require('gulp');
function clean(cb) { // clean函数未被export导出是私有任务
// ... // 它仍然可以被用在 series()组合中。
cb();
}
function build(cb) {
// ... // 它仍然可以被用在 series()组合中。
cb();
}
exports.build = build; // build函数被export导出了是公开任务,可以被 gulp命令直接调用。
exports.default = series(clean, build);
- 公开任务(Public tasks) 从 gulpfile 中被导出(export),可以通过 gulp 命令直接调用。
- 私有任务(Private tasks) 被设计为在内部使用,通常作为 series() 或 parallel() 组合的组成部分。
2、组合任务
Gulp 提供了两个强大的组合方法: series() 和 parallel(),允许将多个独立的任务组合为一个更大的操作。
- 这两个方法都可以接受任意数目的任务(task)函数或已经组合的操作。
- series() 和 parallel() 可以互相嵌套至任意深度。
const { series } = require('gulp');
function transpile(cb) {
// ...
cb();
}
function bundle(cb) {
// ...
cb();
}
exports.build = series(transpile, bundle); //按顺序执行
const { parallel } = require('gulp');
function javascript(cb) {
// ...
cb();
}
function css(cb) {
// ...
cb();
}
exports.build = parallel(javascript, css); //以最大并发来运行
- 如果需要让任务(task)按顺序执行,请使用 series() 方法。
- 对于希望以最大并发来运行的任务(tasks),可以使用 parallel() 方法。
const { series, parallel } = require('gulp');
function clean(cb) {
// ...
cb();
}
function css(cb) {
// ...
cb();
}
function javascript(cb) {
// ...
cb();
}
exports.build = series(clean, parallel(css, javascript)); //可以任意嵌套
五、异步执行
当从任务(task)中返回 stream、promise、event emitter、child process 或 observable 时,成功或错误值将通知 gulp 是否继续执行或结束。如果任务(task)出错,gulp 将立即结束执行并显示该错误。
1、返回 stream(流)
const { src, dest } = require('gulp');
function streamTask() {
return src('*.js')
.pipe(dest('output'));
}
exports.default = streamTask
2、返回 promise(应答)
function promiseTask() {
return Promise.resolve('the value is ignored');
}
exports.default = promiseTask;
3、返回 event emitter(事件发射器)
const { EventEmitter } = require('events');
function eventEmitterTask() {
const emitter = new EventEmitter();
// 发射必须异步,否则gulp无法监听
setTimeout(() => emitter.emit('finish'), 250);
return emitter;
}
exports.default = eventEmitterTask;
4、返回 child process(子进程)
const { exec } = require('child_process');
function childProcessTask() {
return exec('date');
}
exports.default = childProcessTask;
5、返回 RxJS observable(观察对象)
var Observable = require('rx').Observable;
gulp.task('sometask', function() {
return Observable.return(42);
});
6、使用 callback
如果任务(task)不返回任何内容,则必须使用 callback 来指示任务已完成。示例中,callback 将作为cb() 的参数传递给你的任务(task)。
function callbackTask(cb) {
//...
cb();
}
exports.default = callbackTask;
如需通过 callback 把任务(task)中的错误告知 gulp,将 Error 作为 callback 的参数。
function callbackError(cb) {
//...
cb(new Error('kaboom'));
}
exports.default = callbackError;
7、使用 async/await
如果不使用前面提供到几种方式,你还可以将任务(task)定义为一个 async 函数,它将利用 promise 对你的任务(task)进行包装。这将允许你使用 await 处理 promise,并使用其他同步代码。
const fs = require('fs');
async function asyncAwaitTask() {
const { version } = fs.readFileSync('package.json');
console.log(version);
await Promise.resolve('some result');
}
exports.default = asyncAwaitTask;
六、处理文件
gulp 暴露了 src() 和 dest() 方法用于处理计算机上存放的文件。
- 由 src() 产生的流(stream)应当从任务(task)中返回并发出异步完成的信号
const { src, dest } = require('gulp');
exports.default = function() {
return src('src/*.js')
.pipe(dest('output/'));
}
- 流(stream)所提供的主要的 API 是 .pipe() 方法,用于连接转换流(Transform streams)或可写流(Writable streams)。
const { src, dest } = require('gulp');
const babel = require('gulp-babel');
exports.default = function() {
return src('src/*.js')
.pipe(babel())
.pipe(dest('output/'));
}
-
dest()
接受一个输出目录作为参数,当它接收到通过管道(pipeline)传输的文件时,它会将文件内容及文件属性写入到指定的目录中。 - gulp 还提供了
symlink()
方法,其操作方式类似dest()
,但是创建的是链接而不是文件 - 利用 .pipe() 方法将插件放置在 src() 和 dest() 之间,并转换流(stream)中的文件。
- src() 也可以放在管道(pipeline)的中间,以根据给定的 glob 向流(stream)中添加文件。
- 新加入的文件只对后续的转换可用。如果与之前的有重复,仍然会再次添加文件。
const { src, dest } = require('gulp');
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
exports.default = function() {
return src('src/*.js')
.pipe(babel())
.pipe(src('vendor/*.js'))
.pipe(uglify())
.pipe(dest('output/'));
}
- dest() 可以用在管道(pipeline)中间用于将文件的中间状态写入文件系统。
- 此功能可用于在同一个管道(pipeline)中创建未压缩(unminified)和已压缩(minified)的文件。
const { src, dest } = require('gulp');
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
const rename = require('gulp-rename');
exports.default = function() {
return src('src/*.js')
.pipe(babel())
.pipe(src('vendor/*.js'))
.pipe(dest('output/'))
.pipe(uglify())
.pipe(rename({ extname: '.min.js' }))
.pipe(dest('output/'));
}
流动(streaming)、缓冲(buffered)和空(empty)模式。这些模式可以通过对 src()
的 buffer
和 read
参数进行设置。、
- 缓冲(Buffering)模式是默认模式,将文件内容加载内存中。插件通常运行在缓冲(buffering)模式下。
- 流动(Streaming)模式主要用于操作无法放入内存中的大文件,例如巨幅图像或电影。文件内容从文件系统中以小块的方式流式传输,而不是一次性全部加载。
- 空(Empty)模式不包含任何内容,仅在处理文件元数据时有用。
七、文件监控
gulp api 中的 watch() 方法利用文件系统的监控程序(file system watcher)将 globs 与 任务(task) 进行关联。它对匹配 glob 的文件进行监控,如果有文件被修改了就执行关联的任务(task)。
const { watch, series } = require('gulp');
function clean(cb) {
//...
cb();
}
function javascript(cb) {
//...
cb();
}
function css(cb) {
//...
cb();
}
watch('src/*.css', css); // 可以只关联一个任务
watch('src/*.js', series(clean, javascript)); // 或者关联一个任务组合
1、可监控的事件
- 只要创建、更改或删除文件,文件监控程序就会执行关联的任务(task)
- 可以在调用 watch() 方法时通过 events 参数进行指定。
- 可用的有 'add'、'addDir'、'change'、'unlink'、'unlinkDir'、'ready'、'error'。
- 还有一个 'all' 事件,它表示除 'ready' 和 'error' 之外的所有事件。
const { watch } = require('gulp');
// 所有事件都将被监控
watch('src/*.js', { events: 'all' }, function(cb) {
//...
cb();
});
2、初次执行
调用 watch() 之后,任务只有在修改后还会执行。如需在文件修改之前执行,也就是调用 watch() 之后立即执行,请将 ignoreInitial 参数设置为 false
const { watch } = require('gulp');
// 关联的任务(task)将在启动时执行
watch('src/*.js', { ignoreInitial: false }, function(cb) {
//...
cb();
});
3、队列
每一次文件修改只产生一次关联任务的执行并放入队列中。如需禁止队列,请将 queue 参数设置为 false。
const { watch } = require('gulp');
//文件修改后任务都将执行有,可能并发执行
watch('src/*.js', { queue: false }, function(cb) {
//...
cb();
});
4、延迟
文件更改之后,有 200 毫秒的延迟,文件监控程序所关联的任务(task)才会被执行。这是为了避免在同时更改多文件时(例如查找和替换操作)过早启动任务(taks)的执行。
如需调整延迟时间,请为 delay 参数设置一个正整数。
const { watch } = require('gulp');
// 文件修改后等待 500 毫秒执行任务
watch('src/*.js', { delay: 500 }, function(cb) {
//...
cb();
});