逐步详解如何使用 gulp 去进行自动化构建
Posted GoldenaArcher
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了逐步详解如何使用 gulp 去进行自动化构建相关的知识,希望对你有一定的参考价值。
逐步详解如何使用 gulp 去进行自动化构建
在项目开发完,上线前还需要做一系列的准备工作,包括不限于:
- 压缩 CSS, javascript 和 html 文件
- 如果使用 SASS 或是 LESS 的话,还需要对其进行编译,否则整个项目都无法运行
- 同理,如果有对 JavaScript 的版本需求,还需要使用 babel 对 JavaScript 进行转译,否则有些功能同样无法实现
- …
所有的流程全都人力完成的话,一来耗时,二来会介入很多人为错误,所以很多的自动化构建工具应运而生。
Gulp 是基于 node.js 的一个前端自动化构建工具,用于处理上文列举的问题。
gulp 的基础使用
通过学习怎么使用单一任务、组合任务、异步操作去了解怎么完成对 gulp 基础使用的学习。
gulp 的单独任务
gulp 的基础使用,这里 gulp 的版本使用的是 v4.0 之后的版本,用的语法也是 ES6 的语法。
-
新建一个文件夹并且初始化一个 node 项目结构
gulp> mkdir gulp-sample gulp> cd .\\gulp-sample\\ gulp-sample> yarn init
-
下载并安装 gulp 依赖包
因为 gulp 是在开发环境中用来打包项目的,所以只需要在开发环境中安装即可
gulp-sample> yarn add gulp --dev
-
在根目录下新增一个
gulpfile.js
gulpfile.js
是 gulp 的入口文件,这个是必须的。第一步首先尝试在命令行有输出,看看 gulp 是否可以正常运行
gulpfile.js
内容如下:// gulp 的入口文件 exports.default = () => { console.log('hello world'); };
v4.0 后,gulp 通过导出一个函数的方式去定义一个任务,所以这里直接通过 commonJS 导出一个函数即可
导出名为
default
的表明这是默认执行函数注:v3.0 的写法,也就是通过调用
task()
函数传入任务的方式,同样也可以运行,不过使用起来还是箭头函数的写法方便一些const gulp = require('gulp'); gulp.task('default', function () { console.log('hello world'); });
-
运行默认函数
在终端上运行:
gulp-sample> yarn gulp yarn run v1.22.10 $ C:\\assignment\\front\\gulp\\gulp-sample\\node_modules\\.bin\\gulp [04:00:14] Using gulpfile C:\\assignment\\front\\gulp\\gulp-sample\\gulpfile.js [04:00:14] Starting 'default'... hello world [04:00:14] The following tasks did not complete: default [04:00:14] Did you forget to signal async completion? error Command failed with exit code 1. info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
在
[04:00:14] Starting 'default'...
后能看到hello world
的输出,代表了 gulp 的运行是没有问题的。不过在运行完成后还出现了报错信息,说的是:
The following tasks did not complete: default
即 default 这个任务并没有完成。
-
解决 not complete 报错
会出现报错是因为 v4.0 后就取消了同步处理机制,所有的函数被当做异步函数进行处理,因此需要传入一个指示去告知 gulp 的任务已经处理好了
gulp 自己在函数中就包含了这个指示器,调用方法如下:
exports.default = (done) => { console.log('hello world'); done(); // 指示任务完成 };
再次运行:
gulp-sample> yarn gulp yarn run v1.22.10 $ C:\\assignment\\front\\gulp\\gulp-sample\\node_modules\\.bin\\gulp [04:19:00] Using gulpfile C:\\assignment\\front\\gulp\\gulp-sample\\gulpfile.js [04:19:00] Starting 'default'... hello world [04:19:00] Finished 'default' after 2.53 ms Done in 1.17s.
gulp 的组合任务
主要是通过 gulp 提供的两个函数完成,主体代码为:
const { series, parallel } = require('gulp');
const task1 = (done) => {
setTimeout(() => {
console.log('task1 done');
done();
}, 2000);
};
const task2 = (done) => {
setTimeout(() => {
console.log('task2 done');
done();
}, 1000);
};
const task3 = (done) => {
setTimeout(() => {
console.log('task3 done');
done();
}, 1000);
};
series
串行任务
使用串行任务必须完成先指定的任务,再完成下一个任务。应用场景有:先执行对 SASS 的编译,再部署 CSS 代码
exports.seriesTask = series(task1, task2, task3);
终端运行:
gulp-sample> yarn gulp seriesTask
yarn run v1.22.10
$ C:\\assignment\\front\\gulp\\gulp-sample\\node_modules\\.bin\\gulp seriesTask
[05:00:42] Using gulpfile C:\\assignment\\front\\gulp\\gulp-sample\\gulpfile.js
[05:00:42] Starting 'seriesTask'...
[05:00:42] Starting 'task1'...
task1 done
[05:00:44] Finished 'task1' after 2.02 s
[05:00:44] Starting 'task2'...
task2 done
[05:00:46] Finished 'task2' after 2 s
[05:00:46] Starting 'task3'...
task3 done
[05:00:48] Finished 'task3' after 2.01 s
[05:00:48] Finished 'seriesTask' after 6.04 s
Done in 7.22s.
可以看出是有先后执行顺序的,而且尽管 task1 花费的时间比其他的多——task1 需要等待 2 秒,其他两个等待 1 秒——task1 仍然是最先被执行和完成的。
parrllel
并行任务
使用并行任务互不干扰。应用场景有:可以同时进行对 CSS 和 JavaScript 的编译过程
exports.parallelTask = parallel(task1, task2, task3);
终端运行:
gulp-sample> yarn gulp parallelTask
yarn run v1.22.10
$ C:\\assignment\\front\\gulp\\gulp-sample\\node_modules\\.bin\\gulp parallelTask
[05:02:23] Using gulpfile C:\\assignmentF\\front\\gulp\\gulp-sample\\gulpfile.js
[05:02:23] Starting 'parallelTask'...
[05:02:23] Starting 'task1'...
[05:02:23] Starting 'task2'...
[05:02:23] Starting 'task3'...
task2 done
[05:02:24] Finished 'task2' after 1.02 s
task3 done
[05:02:24] Finished 'task3' after 1.02 s
task1 done
[05:02:25] Finished 'task1' after 2.01 s
[05:02:25] Finished 'parallelTask' after 2.01 s
Done in 3.31s.
反观 parallel 这里就不一样,task2 和 task3 比 task1 先运行。
gulp 的异步流程处理操作
gulp 升级到了 v4.0 后都是采取的回调函数的方式,异步地调用每个 task。这也就造成了确认操作是否完成成了个问题。下面列举了三个最常用,也是使用 JavaScript 原生操作的解决方式。
使用回调函数
上文已经提到过,gulp v4.0 之后已经取消了同步操作,而是通过传入一个指示器——done,一个回调函数——的方式去接受该任务已经完成的信号。
因此,当出现错误异常时,也可以通过调用 done()
函数,并且中间抛出异常的方式通知 gulp,具体操作与原生 JavaScript 的回调函数一样,都是错误优先的机制。
需要注意的是,一旦抛出异常,也会终止其他的任务继续运行。
const { series, parallel } = require('gulp');
const task1 = (done) => {
setTimeout(() => {
console.log('task1 done');
done();
}, 2000);
};
const task2 = (done) => {
setTimeout(() => {
console.log('task2 done');
// 修改了这里 ↓
done(new Error('task failed'));
// 修改了这里 ↑
}, 1000);
};
const task3 = (done) => {
setTimeout(() => {
console.log('task3 done');
done();
}, 1000);
};
exports.seriesTask = series(task1, task2, task3);
exports.parallelTask = parallel(task1, task2, task3);
exports.default = (done) => {
console.log('hello world');
done(); // 指示任务完成
};
运行结果:
-
series 报错
gulp-sample> yarn gulp seriesTask yarn run v1.22.10 $ C:\\assignment\\front\\gulp\\gulp-sample\\node_modules\\.bin\\gulp seriesTask [10:00:12] Using gulpfile C:\\assignment\\front\\gulp\\gulp-sample\\gulpfile.js [10:00:12] Starting 'seriesTask'... [10:00:12] Starting 'task1'... task1 done [10:00:14] Finished 'task1' after 2.01 s [10:00:14] Starting 'task2'... task2 done [10:00:15] 'task2' errored after 1.02 s [10:00:15] Error: task failed at Timeout._onTimeout (C:\\assignment\\front\\gulp\\gulp-sample\\gulpfile.js:13:10) at listOnTimeout (internal/timers.js:554:17) at processTimers (internal/timers.js:497:7) [10:00:15] 'seriesTask' errored after 3.03 s error Command failed with exit code 1. info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
可以看到在 task2 这里抛出了异常,task3 没有继续运行
-
parallel 报错
gulp-sample> yarn gulp parallelTask yarn run v1.22.10 $ C:\\assignment\\front\\gulp\\gulp-sample\\node_modules\\.bin\\gulp parallelTask [10:01:09] Using gulpfile C:\\assignment\\front\\gulp\\gulp-sample\\gulpfile.js [10:01:09] Starting 'parallelTask'... [10:01:09] Starting 'task1'... [10:01:09] Starting 'task2'... [10:01:09] Starting 'task3'... task2 done [10:01:10] 'task2' errored after 1.02 s [10:01:10] Error: task failed at Timeout._onTimeout (C:\\assignment\\front\\gulp\\gulp-sample\\gulpfile.js:13:10) at listOnTimeout (internal/timers.js:554:17) at processTimers (internal/timers.js:497:7) [10:01:10] 'parallelTask' errored after 1.03 s error Command failed with exit code 1. info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
可以看到 task2 这里抛出了异常,task1 和 task3 都没有继续运行
使用 promise
使用 promise 可以解决代码中嵌套过深的方法,具体使用方法就是返回 promise 即可:
const { series, parallel } = require('gulp');
const task1 = (done) => {
setTimeout(() => {
console.log('task1 done');
}, 2000);
return Promise.resolve('this will be ignored');
};
const task2 = (done) => {
setTimeout(() => {
console.log('task2 done');
}, 1000);
return Promise.reject(new Error('task2 failed'));
};
const task3 = (done) => {
setTimeout(() => {
console.log('task3 done');
done();
}, 1000);
};
exports.seriesTask = series(task1, task2, task3);
exports.parallelTask = parallel(task1, task2, task3);
运行结果:
gulp-sample> yarn gulp seriesTask
yarn run v1.22.10
$ C:\\assignment\\front\\gulp\\gulp-sample\\node_modules\\.bin\\gulp seriesTask
[10:07:41] Using gulpfile C:\\assignment\\front\\gulp\\gulp-sample\\gulpfile.js
[10:07:41] Starting 'seriesTask'...
[10:07:41] Starting 'task1'...
[10:07:41] Finished 'task1' after 1.41 ms
[10:07:41] Starting 'task2'...
[10:07:41] 'task2' errored after 1.03 ms
[10:07:41] Error: task2 failed
at task2 (C:\\assignment\\front\\gulp\\gulp-sample\\gulpfile.js:14:25)
at bound (domain.js:413:15)
at runBound (domain.js:424:12)
at asyncRunner (C:\\assignment\\front\\gulp\\gulp-sample\\node_modules\\async-done\\index.js:55:18)
at processTicksAndRejections (internal/process/task_queues.js:75:11)
[10:07:41] 'seriesTask' errored after 6.61 ms
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
使用 async/await
既然可以使用 promise,那么也可以使用 ES7 提供的语法糖:async/await,只要 node 环境是 8 以上,就可以使用 async/await,用法如下:
const { series, parallel } = require('gulp');
const timeout = (time) => {
return new Promise((resolve) => {
setTimeout(resolve, time);
});
};
const task1 = async (done) => {
await timeout(2000);
console.log('task1 done');
};
const task2 = async (done) => {
await timeout(1000);
console.log('task2 done');
};
const task3 = async (done) => {
await timeout(1000);
console.log('task3 done');
};
exports.seriesTask = series(task1, task2, task3);
运行结果:
gulp-sample> yarn gulp seriesTask
yarn run v1.22.10
$ C:\\assignment\\front\\gulp\\gulp-sample\\node_modules\\.bin\\gulp seriesTask
[10:14:36] Using gulpfile C:\\assignment\\front\\gulp\\gulp-sample\\gulpfile.js
[10:14:36] Starting 'seriesTask'...
[10:14:36] Starting 'task1'...
task1 done
[10:14:38] Finished 'task1' after 2.02 s
[10:14:38] Starting 'task2'...
task2 done
[10:14:39] Finished 'task2' after 1.01 s
[10:14:39] Starting 'task3'...
task3 done
[10:14:40] Finished 'task3' after 1.01 s
[10:14:40] Finished 'seriesTask' after 4.05 s
Done in 5.20s.
可以看出,通过使用 async/await 的语法糖后,代码变得更加的简洁,可读性也高了。
gulp 构建过程核心
其实对于文件的处理,逻辑上都是共通的,步骤总归是:
- 将文件手动复制粘帖到压缩工具中
- 获取压缩过后的结果
- 将文件粘贴回需要上线的文件夹中
使用 gulp 的原理也是这样,只不过原本由人工操作的部分转嫁给了机器(代码),速度更快,出错率也更低。
这里会通过使用 node 原生的 API 去模拟一下 gulp 内部的实现过程。
-
创建文件的读取也写入流
这一步就是文件的 IO,不是很难:
const fs = require('fs'); exports.default = () => { // 文件读取流 const read = fs.createReadStream('normalize.css'); // 文件写入流 const write = fs.createWriteStream('normalize.min.css'); // 将读取出来的文件放入写入流 read.pipe(write); return read; };
这里导出的是 default,所以运行方式依然是
yarn gulp
运行结果:
gulp-sample> yarn gulp yarn run v1.22.10 $ C:\\assignment\\front\\gulp\\gulp-sample\\node_modules\\.bin\\gulp [10:32:21] Using gulpfile C:\\assignment\\front\\gulp\\gulp-sample\\gulpfile.js [10:32:21] Starting 'default'... [10:32:21] Finished 'default' after 9.68 ms Done in 1.53s.
能够看到一个与 normalize.css 内容一样的文件被导出。
这里使用的 normalize.css 用的是 CSS 初始化的两种常见方法 中的内容,出于测试目的,任何的文件都可以使用。
-
压缩文件
这一步会通过使用 stream 的 API: Transform 进行实现。
const fs = require('fs'); // 添加文件转换流 const { Transform } = require('stream'); exports.default = () => { // 文件读取流 const read = fs.createReadStream('normalize.css'); // 文件写入流 const write = fs.createWriteStream('normalize.min.css'); // 文件转换流 const transform = new Transform({ transform: (chunk, encoding, callback) => { // chunk => buffer const input = chunk.toString(); const output = input // 去除所有空格 .replace(/\\s+/g, '') // 去除所有备注 .replace(/\\/\\*.+?\\*\\//g, ''); callback(null, output); }, }); // 将读取出来的文件,先经过压缩,再放入写入流 read.pipe(transform).pipe(write); return read; };
运行命令依旧一样,运行结果就不一样了:
这时候 normalize.min.css 已经被压缩过了,而没有保留原本的格式。
可以看出来,gulp 的使用核心就是通过文件操作的方式去进行的。
gulp 文件操作 api
gulp 主要
以上是关于逐步详解如何使用 gulp 去进行自动化构建的主要内容,如果未能解决你的问题,请参考以下文章