gulp 的使用(一):起步

1,845 阅读6分钟

这是我参与更文挑战的第14天,活动详情查看: 更文挑战

前言

在这之前,我已经写好了一系列有关 webpack5 的文章(新手向),webpack5 可谓是现在的最流行的前端构建工具,生态完整,有各种 plugin 和 loader 可以实现各种功能。

但是除了 webpack,还有没有其他的构建工具可以用?

当然有,gulp 就是其中之一。

gulp 与 webpack 的根据依赖来管理资源不同,gulp 是流水线性化管理资源,着重于控制整个打包构建过程,使用流式(stream)处理整个构建过程。gulp 相比 webpack 而言,gulp 更加容易理解以及使用简单。

但很遗憾,gulp 只是一个流水性加工工厂,它自身无法处理模块化,尽管它可以将模块化任务交给 webpack-stream(其实就是 webpack,与 gulp 集成) 来处理,但是一边使用流水性管理资源,一边使用模块化管理资源,这种不伦不类的开发风格实在太诡异(后面文章会有介绍)。

因此如果你确定你的项目需要使用模块化,那么我不建议你使用 gulp,webpack 是更好的选择。当然如果你确定你的模块化依赖于浏览器原生 esm,不需要考虑浏览器兼容性,使用 gulp 也是可以走得通,不过我还是不建议这样做。

本系列将分多篇文章,逐一介绍 gulp 的使用。
本系列比较新手向,适合从来没有使用过 gulp 或略知一二的人看,不适合希望深入 gulp 的人看。

安装

如果你没有安装 node,请先移步安装 node。

新建一个项目 gulp-demo,进入项目目录,初始化。

npm init

全局安装 gulp-cli,再本地安装 gulp。

npm install gulp-cli -g
npm install gulp -D

在本地项目根目录,运行以下命令,创建 gulpfile.js(其实你也可以自己手动新建文件)

npx -p touch nodetouch gulpfile.js

gulpfile.js 即是 gulp 的配置文件,在里面需要用原生 js 去写配置,当运行 gulp 命令时,会自动加载此配置文件。

事实上,官方文档也有提到可以用 typescript、babel 等需要转译语言来写配置文件,文件名则分别为 gulpfile.tsgulpfile.babel.js。不过个人建议还是用纯 js 版本吧。

任务

在 gulpfile.js 配置文件里,我们需要配置一个个任务来完成管理资源的工作,其中任务分为私有任务、公有任务和组合任务。

公有任务

我们在 gulpfile.js 写上一个函数。

// 公有任务
function publicTask(cb) {
  console.log('公有任务')
  cb()
}

exports.publicTask = publicTask

导出 publicTask,即可使用命令 gulp publicTask 运行 publicTask 任务。

运行

gulp publicTask

image.png

publicTask 函数就是公有任务,因为它是暴露出去的,可以在终端使用命令 gulp publicTask 来调用。

记得导出什么就用什么来调用,比如:exports.aa = publicTask,则用 gulp aa 来调用 publicTask 任务。

注意:上面的 cb 参数是指 callback 回调函数,如果任务不返回任何内容,必须调用 cb 回调来表明任务执行成功,否则将会报错。

私有任务

我们再在 gulpfile.js 写上一个“私有任务”函数。

...

// 私有任务
function privateTask(cb) {
  console.log('私有任务')
  cb()
}

...

exports.default =  privateTask

这里的导出 default 是一个 privateTask 函数,运行 gulp 命令会默认调用这个函数。

运行命令

gulp

image.png

我们可以看到“私有任务”打印在终端上。

privateTask 是一个私有任务,因为它只能通过 gulp 调用,它没有像 publicTask 可以通过 gulp publicTask 调用。

看起来公有任务和私有任务的界限有些不够清楚,但在实际开发中,其实我们并不需要搞清他们的界限,知道如何去暴露和调用就可以了。

组合任务

组合任务分为“串行任务”和“并行任务”。

串行任务

串行任务就是按顺序依次执行任务,gulp.series 是用来处理串行任务。

我们把之前的 exports.default 修改一下,使用 gulp.series 方法。

gulpfile.js

const { series } = require('gulp')

...

// exports.default =  privateTask
exports.default = series(privateTask, publicTask)

运行命令

gulp

image.png

可以看到“私有任务”先打印了出来,“公有任务”后打印出来,可以看出,gulp.series 的执行任务顺序是从左到右或从上到下。

并行任务

并行任务则是多个任务同时执行,gulp.parallel 是用来处理并行任务。

gulpfile.js

// exports.default = gulp.series(privateTask, publicTask)
exports.default = gulp.parallel(privateTask, publicTask)

下面贴出“串行任务”和“并行任务”的比较,似乎“并行任务”执行所用的时间会短一点。

image.png

串行任务与并行任务的任意组合

当然,组合任务的使用方法并不止上述这么简单使用,实际上我们可以混用串行任务和并行任务,例如:

exports.default = series(privateTask, publicTask, parallel(privateTask, series(publicTask)))

旧版本的任务

在旧版本的 gulp,我们使用 task 方法将函数注册为任务,例如:

const { series, task } = require('gulp')

task('privateTask', function(cb) {
    console.log('私有任务')
    cb()
})

task('default', series('privateTask'))

现在新版本的 gulp 虽然支持旧版本的 task,但多以 exports 为注册方式为主,建议使用新版本的方式。

读取文件和输出文件

gulp 有两个方法用于处理文件,一个是 src,另一个是 dest,这两个方法分别用来读取文件和输出文件。

我们先在项目根目录下创建一个 src 目录,创建一个 index.html 文件。

为了防止之前的代码骚扰,我们先把 gulpfile.js 的代码注释或删掉,重新写过代码

在 gulpfile.js 配置。

const { series, parallel, src, dest } = require('gulp')

function html() {
  return src('src/**/*.html')
    .pipe(dest('dist'))
}

exports.build = series(html)

运行以下命令

gulp build

可以发现在目录里多了一个 dist 目录,并且里面有一个 index.html 文件。

image.png

上述代码的 src 就是用来读取 src 目录里的所有 html 文件,将文件转换成流,然后 dest 就是用来将流转换为文件输出到目标路径。

拓展:上面的 src('src/**/*.html') 中的字符串被 gulp 称为 glob 字符串,glob 字符串是用来匹配文件路径,具体规则看这篇官方文档

管道

在上面的小节里还用到了 pipe 方法,pipe 即是管道,管道是用于连接“转换流”或者“可写流”,比如上一小节的 pipe 就是用来连接 dest 的转换流(将流转换为文件)。

总之,你可以简单认为管道是流的一个载体,我们需要处理流的内容时或将流转换为文件时,都需要使用管道。

在后面的文章里,将会使用大量的管道,在后面的代码里,你将会更加理解管道这一概念。

完整项目

2021.8.30,我重新整理了一遍项目,已放到 gitee 上,大家可以 clone 下来直接用,代码的提交记录顺序和我这个系列文章教程顺序是一致的,大家看到哪一篇文章时,就回滚代码到哪一个版本,这样看项目代码会更直观。

gitee 库链接:gitee.com/only1zhuo/g…

image.png

“gulp 的使用”系列文章