微信号:cssmagic_wx

介绍:前端进阶,你我同行.每天迈出一小步,不断收获新技能!

[译] Gulp 4 入门指南

2016-06-21 20:16 |
不想错过更多好文?请点击上面的 “CSS魔法” 订阅公众号。

介绍

Gulp 是一款基于 JavaScript 的构建工具,它主要用于自动化运行 Web 开发中的各项任务。但其实 Node.js 所能做的任何事情都可以通过 Gulp 实现自动化。由于 Node.js 可以执行 shell 命令,因此,Gulp 几乎可以自动化所有任务。不过在实际使用中,Gulp 主要还是在 Web 开发领域用得更多一些。

Gulp 与 Grunt

Gulp 的主要竞争对手是 Grunt,它和 Gulp 一样是免费开源的。其他不怎么火的竞争者还有 Broccoli、Brunch、Cake 和 Jake 等等。

Gulp 和 Grunt 之间的一大差异在于它们的配置方式。它们都需要一个 JavaScript 文件来进行配置,比如 Gulp 的配置文件叫作 gulpfile.js,而 Grunt 用的是 Gruntfile.js。Gulp 是通过调用多个不同的函数来完成配置的,而 Grunt 则需要把一坨巨大的对象字面量传给一个函数来完成配置。

在正常情况下,Gulp 的运行速度比 Grunt 要快,这归功于 “流”(stream)的使用。在运行连续任务时,Grunt 所采用的方式是生成大量的临时文件,以便向后续步骤传递数据。而采用流则允许后续步骤更早地开始,只要流中存在有效的数据即可开始,无需等待整个文件被写完。

Gulp 与 Node.js

如果要使用 Gulp,就必须安装 Node.js。由于 Node.js 是跨平台的,Gulp 也可用于多种平台,包含 Windows、Mac OS X 和 *nix 操作系统。

如果 Node.js 打开了 --harmony 开关,我们还可以使用 ES6(也就是 ES 2015)的部分特性。如何让 Gulp 用上 ES6 呢?如果你是在 *nix 环境下,那就需要像这样创建一个别名:alias gulp6='node --harmony $(which gulp)'。你最好把这行代码放进 .bashrc 文件中,这样你在每次打开终端时都可以直接使用这个别名。以后记得用 gulp6 来代替 gulp 命令,以便享受 ES6 带来的新特性。而在 Windows 环境下,你可以尝试 doskey 命令来实现类似的别名效果。

(译注:在较新的 Node 版本中,很多 ES6 特性已经是默认开启的了。)

本文的某些例子将用到 ES6 的一项新特性,也就是箭头函数表达式。这个特性允许我们使用一种更简洁的方式来书写匿名函数,并且它还有其他的优点(比如:词法作用域的 this)。对于本文,你只需知道 ES5 语法中的 function () { return expression; } 大体上等价于 ES6 中的 () => expression

插件生态

一般来说,我们会用到插件来实现各种 Gulp 任务。截止到 2015 年 5 月 25 日,已经有 1711 个 Gulp 插件可供使用。在 *nix 环境下,你可以执行如下命令来查询最新的插件数量:

npm search gulpplugin | wc-l

我们可以使用 Gulp 来完成以下常见任务:

  • 校验 HTML、CSS、JavaScript 和 JSON 文件的语法。
  • 将 ES6 JavaScript 代码编译为 ES5(通过使用 Babel、Traceur 或者 TypeScript)。
  • 运行单元测试和端到端测试。
  • 合并和压缩 CSS、JavaScript 文件。
  • 通过 HTTP 来提供静态文件服务。
  • 执行 shell 命令。
  • 监听特定文件(或特定文件类型)的变动,当发现文件改变时运行特定任务。
  • 当这些有变化的文件被处理后刷新浏览器(LiveReload)。

(译注:关于 Gulp 插件,下文将会详述。)

在撰写这篇文章时,Gulp 最新的稳定版本为 3。本文介绍的目标是最新的开发版本 4。Gulp 4 并不向后兼容 3,因此升级后你需要修改 gulpfile.js。当然,大部分面向 v3 的插件仍然可以在 v4 中正常使用。

如果你用的是 Windows,那么本文所述的 “终端” 实际上对你来说就是 “命令提示符(Command Prompt)” 程序了。

安装 Gulp

在执行 npm install -g gulp 之后,就可以在命令行使用 gulp 命令了。这个操作将安装最新的稳定版本。

不过本文是在讨论 Gulp 4,而它还没有正式发布成为稳定版,因此我们需要通过以下步骤来安装它:

  1. 打开终端。
  2. 确定已正确安装 Git。(译注:Mac OS X 自带 Git。)
  3. 如果你之前安装过 Gulp,则需要使用 npm uninstall -g gulp 命令来卸载旧版本。
  4. 执行 npm install -g gulpjs/gulp-cli#4.0 命令,安装 Gulp 4。

如果你只想在某个项目的范围之内使用 Gulp 4,那就这样做:

  1. 打开终端。
  2. 进入这个工作项目的顶层目录。
  3. 如果该项目还没有 package.json 文件,则需要通过 npm init 命令来新建一个。在此过程中会有一些命令行交互。
  4. 如果之前该项目已安装过低于 4.x 版本的 Gulp,那么先使用 npm uninstall gulp --save-dev 指令卸载它,再安装 Gulp 4。
  5. 以 “局部安装” 的方式把 Gulp 作为一个依赖添加到 package.json 文件中,命令如下:npm install gulpjs/gulp.git#4.0 --save-dev(译注:原文所述步骤有误,译文已作修正。)
  6. 创建 gulpfile.js(后面会详细介绍)。

npm install--save-dev 参数将在 package.json 中添加一个开发依赖。这使得团队内的其他开发者可以通过 npm install 命令来安装该项目所需的所有依赖。

运行 Gulp

各项 Gulp 任务都是由 gulpfile.js 来定义的。在尝试运行各项任务之前,我们不妨进入这个工作项目的顶层目录或某个子目录内,先来熟悉一下 Gulp 的基本操作。

执行 gulp --helpgulp -h 可以获取基本的帮助信息。

执行 gulp --versiongulp -v 可以查看本机安装的 Gulp 版本号。此命令会同时显示(以全局方式安装的)命令行程序的版本号和该项目自身(以局部方式安装)的 Gulp 的版本号。

执行 gulp --tasksgulp -T 可以查看 gulpfile.js 文件所定义的所有任务。此命令将以依赖树的形式呈现所有任务。如果你希望按照定义顺序、以平铺的方式来查看任务列表,请执行 gulp --tasks-simple

执行 gulp --verify 可以检查当前项目是否用到了被列入黑名单的 Gulp 插件。此命令将检查 package.json 文件指定的所有依赖。

执行 gulp [options] task1 task2 可以运行 gulpfile.js 所定义的任务。除了任务名外,我们还可以附加一些可选的选项(译注:即 [options] 这个部分),不过通常来说用不到。

如果我们同时指定了多个任务名,则它们将被并行运行。如果多个任务需要以串行的方式运行,那么我们就需要在 gulpfile.js 中定义一个新任务来指定这几个任务的运行顺序,然后在命令行中单独运行这个新任务。

如果没有指定任务名,则会有一个默认任务被运行,稍后我们会讨论如何定义一个默认任务。如果不指定具体的任务,并且没有定义 default 任务,那会显示一条错误信息。

大部分任务运行结束后 Gulp 将退出。但某些任务是例外,例如 connect(用于提供 HTTP 静态资源服务)和 watch(用于监听文件是否有改动)等等,Gulp 将不会自动退出,这些任务会一直运行,直到被中止(译注:或者任务自己出错退出)。你可以按 Ctrl-C 组合键来中止任务并让 Gulp 退出。

Gulp 插件

Gulp 拥有大量的插件可供选用,为了缓解你的选择困难症,我直接推荐一些优秀插件给你吧:

  • gulp-babel - 将 ES6 编译为 ES5。
  • gulp-changed - 过滤掉比目标文件旧的文件(只处理有更新的文件)。
  • gulp-concat - 合并 CSS 和 JavaScript 文件。
  • gulp-csslint - 校验 CSS 代码质量。
  • gulp-eslint - 使用 ESLint 来校验 JavaScript 代码质量。
  • gulp-jasmine - 运行 Jasmine 测试。
  • gulp-jshint - 使用 JSHint 来校验 JavaScript 代码质量。
  • gulp-jscs - 使用 JSCS 来校验 JavaScript 代码风格。
  • gulp-less - 将 LESS 文件编译为 CSS。
  • gulp-livereload - 当调用 livereload 方法时,刷新监听 livereload 事件的浏览器。
  • gulp-plumber - 允许 Gulp 在发生错误之后继续运行。
  • gulp-sourcemaps - 和成 sourcemap 文件,便于调试源码(因为 ES6、CoffeeScript、TypeScript 等格式的源码需要编译为 ES5 才能在浏览器中运行)。
  • gulp-uglify - 压缩 JavaScript 文件。
  • gulp-usemin - 将 HTML 文件中 CSS 和 JS 文件的路径替换为对应的 min 版本(已经合并、压缩过的版本)。
  • gulp-watch - 监听文件是否被修改,并且在文件修改时运行指定任务。

此外,有个叫 del 的 npm 包也十分常用。我们可以用它来删除指定的目录和文件。

我们可以在 http://gulpjs.com/plugins 这个页面搜索 Gulp 插件。所有以 "gulpplugin" 关键词发布到 npm 的插件都会自动被收录到这个页面。这个页面的好处在于,点击任一插件名均可跳至该插件的文档页面。搜索插件的另外一条路是使用 npm search 命令。比如说,执行 npm search gulpplugin lint 即可找到所有具有 linting 功能的插件。

执行 npm install plugin-name —save-dev 即可安装一个插件。插件将被安装到项目的 node_modules 目录。一旦某个插件安装好之后,我们就可以在 gulpfile.js 文件中引入(require)该插件,并将它运用到一个或多个任务中。比如像这样:var foo = require('gulp-foo');

其实还有一种更好的办法来 require 插件,就是使用 gulp-load-plugins 这个库。有了它之后,我们就不必为每个插件添加一行 require 语句了。这个小工具可以自动加载 package.json 中所有以 gulp- 开头的依赖项(译注:当然,除了它自己以外),并返回一个对象,对象的各个属性就对应了各项依赖。它还具有懒加载的特性——各个插件只有在被用到时才会真正加载,而没有用到的插件是不会被加载的。

var pi = require('gulp-load-plugins')();

在拿到 pi 这个对象后,我们在定义任务时就可以使用 pi.name 来引用某个名为 gulp-name 的插件。

Gulp 的 API

(译注:以下 API 均为以 Gulp 4.0 版本为准。)

Gulp 所提供的 JS API 是由 GulpUndertaker 这两个类所提供的。

Gulp 这个类提供了 srcdestwatch 方法。这个类的源码位于 Gulp 项目根目录的 index.js 文件中(对 Gulp 4 来说,项目根目录就是 https://github.com/gulpjs/gulp/tree/4.0)。这个类继承自 Undertaker 这个类。

Undertaker 这个类提供了 taskseriesparallelgetsettreeregistry 方法。这个类的源码位于 Undertaker 项目根目录的 index.js 文件中(根目录在这里:https://github.com/phated/undertaker)。Undertaker 类继承自 Node 的核心类 EventEmitterhttps://nodejs.org/api/events.html)。

如果只是为了使用 Gulp,不一定要了解这些继承关系;但理解这些关系将有助于我们理解其中某些方法的使用。

Gulp 还用到了另一个重要的 npm 模块,就是 vinyl-fs。这个模块使用 Vinyl 对象来存储元数据,这些元数据用于描述文件。Vinyl 适配器使得我们可以以 “流” 的方式来读写 Vinyl 对象的内容。“源文件流负责生产文件对象,目标文件流负责消费文件对象。” 更具体的信息可以参考 https://github.com/wearefractal/vinyl-fshttps://github.com/wearefractal/vinylhttps://medium.com/@contrahacks/gulp-3828e8126466

下面这行代码可以获取 Gulp 对象:

var gulp = require('gulp');

这个对象支持 Gulp 类、Undertaker 类和 EventEmitter 类所定义的所有方法。

在介绍具体的方法之前,我们需要简单理解一下 glob(文件匹配符)。Gulp 的很多方法都接受 glob 作为参数,这个参数可以是一个字符串,或是一个字符串数组。字符串中可以包含我们比较熟悉的通配符。Glob 的底层实现是由 npm 模块 node-glob 来提供的。更详细的语法请自行查阅《Glob Primer》这篇文档。其基本语法包括:

  • ? 代表任意一个字符。
  • * 代表 0 个或多个任意字符。
  • ** 作为路径的一部分,它表示任意数量层级的目录。

src 方法

src 方法会提供一个由 Vinyl 对象组成的流,这些流将通过管道传递(pipe)给插件处理。这个方法接受一个 glob 和一个选项对象作为参数。Glob 用于指定输入文件,以备处理;而选项参数则用于传递给 node-glob 模块。关于这个选项参数的详细信息请参见 https://github.com/gulpjs/gulp/blob/master/docs/API.md#gulpsrcglobs-optionshttps://github.com/isaacs/node-glob。一般来说我们不需要指定特别的选项,而且这个参数也是可以省略的。

dest 方法

dest 方法接受管道传递过来的流,并将流数据输出至文件。所有传递给它的数据都会被重新转发,这允许我们多次调用 dest 方法来将数据输出至多个位置。他接受一个目标路径和一个选项对象作为参数。目标路径用于指定了输出文件的路径。而关于选项参数的详细信息请参见 https://github.com/gulpjs/gulp/blob/master/docs/API.md#gulpdestpath-options。一般来说我们不需要指定特别的选项,而且这个参数也是可以省略的。

watch 方法

watch 方法可以监听文件,当文件被改动时可以运行指定的任务(调用某个函数)。它接受一个 glob、一个选项对象和一个函数作为参数。Glob 用于指定哪些文件需要被监听。这个方法实际上是通过一个名为 gaze 的 npm 模块来实现的。关于那个选项参数,请在 https://github.com/shama/gaze 查阅 gaze.Gaze 构造函数的选项。一般来说我们不需要指定特别的选项,而且这个参数也是可以省略的。(译注:后来 Gulp 4 在底层把 gaze 换成了 chokidar。)

task 方法

task 方法用来定义任务。它接受一个字符串(任务名)和一个函数作为参数。当运行某个任务时,该任务对应的函数就将被调用。这个函数可以是匿名函数,也可以是在别处已经声明过的函数。如果在调用 task 方法时没有传入函数,则它的作用相当于一个 getter,返回先前为某个任务名定义的任务函数。

series 方法

series 方法会返回一个函数。这个函数被调用时,将会以串行的方式运行指定的任务。它接受任意数量的参数,参数可以是任务名,也可以是函数。由于它返回一个函数,因此它在调用时通常是作为参数传递给其他方法(比如 task 方法)的。

parallel 方法

parallel 方法会返回一个函数。这个函数被调用时,将会以并行的方式运行指定的任务。它接受任意数量的参数,参数可以是任务名,也可以是函数。由于它返回一个函数,因此它在调用时通常是作为参数传递给其他方法(比如 task 方法)的。

接下来我们来看一下 Undertaker 类所定义的方法,这些方法通常并不会在 gulpfile.js 中直接使用。

get 方法接受一个任务名(字符串)作为参数,返回与该任务名对应的函数。

set 方法用于设置(或修改)某个给定任务名所对应的函数。它接受一个任务名(字符串)和一个函数作为参数。如果某个任务名已经定义,那么原先设置的函数将被替换掉。

tree 方法返回一个数组,该数组由已定义的任务名字符串所组成。它接受一个选项对象作为参数。如果 deep 选项设置为 true,那么返回的数组将由多个对象组成,这些对象展现了每项任务的依赖关系。Gulp 的命令行选项 --tasks--tasks-simple 其实就用到了这个方法。

registry 方法可以获取(或设置)任务名和任务函数之间的映射关系。

定义 Gulp 任务

Gulp 是运行在 Node.js 上的,因此 gulpfile.js 中可以包含任何 Node.js 可以处理的代码。这意味着所有的 Node.js 核心模块和 npm 模块都可以使用。

如下所示,定义一个 Gulp 任务其实就是这么简单:

var gulp = require('gulp');

gulp.task('hello', function () {    console.log('Hello, World!');
});

而这个任务如果用 ES6 来写,会是这样的:

let gulp = require('gulp');

gulp.task('hello', () => console.log('Hello, World!'));

如果要运行这个任务,执行以下命令就可以了:

gulp hello

定义 Gulp 任务通常有以下三种形式:

gulp.task(name, function () { ... });

gulp.task(name, gulp.series(...));

gulp.task(name, gulp.parallel(...));

一个 Gulp 任务往往需要读取特定的文件,对文件内容采取一个或多个的操作,然后生成一个或多个输出文件。这类任务通常看起来会是下面这个样子:

gulp.task(name, function () {    return gulp.src(srcPath).
        pipe(somePluginFn()).
        pipe(anotherPluginFn()).
        pipe(gulp.dest(destPath));
});

在 ES6 环境下,我们也可以这么写:

gulp.task(name, () =>
    gulp.src(srcPath).
        pipe(somePluginFn()).
        pipe(anotherPluginFn()).
        pipe(gulp.dest(destPath)));

通过 Gulp 提供静态资源服务

有许多 npm 模块可以通过 HTTP 协议来向外提供静态文件服务。一个常见的选择是 connect(该项目位于 https://github.com/senchalabs/connect)。以下命令可以安装必要的模块:

npm install connect --save
npm install serve-static -save

下面这个 Gulp 任务将在项目的顶层目录向外提供静态文件服务:

var connect = require('connect');var http = require('http'); // a Node.js core modulevar serveStatic = require('serveStatic');
gulp.task('connect', function () {    var app = connect();
    app.use(serveStatic(__dirname));    var port = 8080;
    http.createServer(app).listen(port);
});

__dirname 是一个 Node.js 变量,它的值就是当前路径。如果在当前目录下有一个 index.html 文件,那么我们在浏览器地址栏键入 http://localhost:8080 应该就可以访问到它。

如果要运行这个任务,执行 gulp connect 即可。

监听文件的变更

Gulp 可以监听文件的改变或新文件的创建。但如果 gulpfile.js 文件本身被改动了,那只有重启 Gulp 才能让修改过的 gulpfile.js 生效。

下面这个 Gulp 任务用于监听 LESS 文件的变动。一旦侦测到了文件变更,Gulp 就会运行我们预先定义好的 lesscsslint 任务。

gulp.task('watch', function () {
    gulp.watch('styles/*.less', gulp.series('less', 'csslint'));
})

实时刷新(Live Reload)

Gulp 还可以让浏览器自动刷新。当我们在修改 HTML、CSS、JavaScript 等等由浏览器加载的文件时,这个功能尤为实用。有不少 Gulp 插件支持这个功能。最常见的选择是 gulp-livereload(该项目位于 https://github.com/vohof/gulp-livereload)。这个插件与 Chrome 浏览器的配合最为顺滑,不过你需要提先装好 livereload 的 Chrome 扩展。我们在 https://chrome.google.com/webstore/category/apps 这个页面搜索 “livereload” 就可以找到这个扩展了。

请按照以下步骤来配置这个插件:

  1. 安装 gulp-livereload 插件。
  2. 在 HTML 入口文件中添加这个 script 标签: <script src="http://localhost:35729/liverload.js"></script>
  3. watch 任务内调用 livereload.listen() 函数。
  4. 在文件有改动且需要刷新浏览器时,调用 livereload() 函数。

下面有一个 gulpfile.js 文件的示例,它展示了上述步骤的最后两步。这个文件定义了多个 Gulp 任务,这些任务在日常的 Web 开发中十分实用。

gulpfile.js 示例

var connect = require('connect');var del = require('del');var gulp = require('gulp');var http = require('http');var pi = require('gulp-load-plugins')();var serveStatic = require('serve-static');var paths = {
    build: 'build',
    css: 'build/**/*.css',
    html: ['index.html', 'src/**/*.html'],
    js: ['src/**/*.js'],
    jsPlusTests: ['src/**/*.js', 'test/**/*.js'],
    less: 'src/**/*.less',
    test: 'build/**/*-test.js'};// This just demonstrates the simplest possible task.// 这是一个最简单的 Gulp 任务。gulp.task('hello', function () {    console.log('Hello, World!'));
});// This deletes all generated files.// In tasks that do something asynchronously, the function// passed to task should take a callback function and// invoke it when the asynchronous action completes.// This is how gulp knows when the task has completed.// 这个任务会删除所有构建生成的文件。// 在定义异步任务时,任务函数应该接受一个回调函数,// 任务函数在运行完毕后需要调一下这个回调函数,// 这样 Gulp 才知道这个任务已经结束了。gulp.task('clean', function (cb) {
    del(paths.build, cb);
});// This starts a simple HTTP file server.// 这个任务会启动一个简单的 HTTP 文件服务器。gulp.task('connect', function () {    var app = connect();
    app.use(serveStatic(__dirname));
    http.createServer(app).listen(1919);
});// This validates all CSS files.// In this example, the CSS files are generated from LESS files.// 这个任务会校验所有的 CSS 文件。// 在这个例子中,CSS 文件都是由 LESS 文件编译生成的。gulp.task('csslint', function () {    return gulp.src(paths.css).
        pipe(pi.csslint({ids: false})).
        pipe(pi.csslint.reporter());
});// This validates JavaScript files using ESLint.// 这个任务会调用 ESLint 来校验 JavaScript 文件。gulp.task('eslint', function () {    return gulp.src(paths.jsPlusTests).
        pipe(pi.changed(paths.build)).
        pipe(pi.eslint({
            envs: ['browser', 'ES6', 'node'],
            rules: {
                curly: [2, 'multi-line'],
                indent: [2, 2]
            }
        })).
        pipe(pi.eslint.format());
});// This is used by the "watch" task to// reload the browser when an HTML file is modified.// 这个任务是用来被 "watch" 任务触发的,// 从而实现当 HTML 被修改时自动刷新浏览器的效果。gulp.task('html', function () {    return gulp.src(paths.html).
        pipe(pi.livereload());
});// This validates JavaScript files using JSHint.// 这个任务会调用 JSHint 来校验 JavaScript 文件。gulp.task('jshint', function () {    return gulp.src(paths.jsPlusTests).
        pipe(pi.changed(paths.build)).
        pipe(pi.jshint()).
        pipe(pi.jshint.reporter('default'));
});// This compiles LESS files to CSS files.// 这个任务会把 LESS 文件编译成 CSS 文件。gulp.task('less', function () {    return gulp.src(paths.less).
        pipe(pi.changed(paths.build)).
        pipe(pi.less()).
        pipe(gulp.dest(paths.build)).
        pipe(pi.livereload());
});// This compiles ES6 JavaScript files to ES5 JavaScript files.// "transpile" is a term used to describe compiling// one syntax to a different version of itself.// Compiling ES6 code to ES5 fits this description.// 这个任务会把 ES6 的 JavaScript 文件编译成 ES5 的 JavaScript 文件。// "transpile"(转译)这个术语表示把一种语法编译为这种语法的另一个版本。// 把 ES6 代码编译成 ES5 正好符合这种情况。gulp.task('transpile-dev', function () {    return gulp.src(paths.jsPlusTests).
        pipe(pi.changed(paths.build)).
        pipe(pi.sourcemaps.init()).
        pipe(pi.babel()).
        pipe(pi.sourcemaps.write('.')).
        pipe(gulp.dest(paths.build)).
        pipe(pi.livereload());
});// This does the same as the previous task, but also// concatenates and minimizes the resulting JavaScript files.// 这个任务与上个任务的功能基本一致,// 但这个任务还会把编译产生的 JavaScript 文件合并、压缩起来。gulp.task('transpile-prod', function () {    return gulp.src(paths.js).
        pipe(pi.sourcemaps.init()).
        pipe(pi.babel()).
        pipe(pi.concat('all.js')).
        pipe(pi.uglify()).
        pipe(pi.sourcemaps.write('.')).
        pipe(gulp.dest(paths.build));
});// This is not meant to be used directly.// Use the "test" task instead.// 这个任务并不是用来直接运行的。// 我们应该使用 "test" 这个任务来代替。gulp.task('jasmine', function () {    return gulp.src(paths.test).
        pipe(pi.plumber()).
        pipe(pi.jasmine());
});

gulp.task('test', gulp.series('transpile-dev', 'jasmine'));// This watches HTML, LESS, and JavaScript files for changes// and processes them when they do.// It also reloads the web browser.// 这个任务会监听 HTML、LESS 和 JavaScript 文件的变动,// 并在它们有变动的时候处理它们。// 这个任务也会自动刷新浏览器。gulp.task('watch', function () {
    pi.livereload.listen();
    gulp.watch(paths.html, gulp.series('html'));
    gulp.watch(paths.less, gulp.series('less', 'csslint'));
    gulp.watch(paths.jsPlusTests,
        gulp.series('eslint', 'jshint', 'transpile-dev'));
});// This compiles LESS and ES6 JavaScript files in parallel.// 这个任务会以并行的方式编译 LESS 和 ES6 JavaScript 文件。gulp.task('build-dev', gulp.parallel('less', 'transpile-dev'));// This does the same as the previous tasks, but also// concatenates and minimizes the resulting JavaScript files.// 这个任务与上个任务的功能基本一致,// 但这个任务还会把编译产生的 JavaScript 文件合并、压缩起来。gulp.task('build-prod', gulp.parallel('less', 'transpile-prod'));// This is used when gulp is run without specifying a task.// It runs the "build-dev" task and then// starts the "connect" and "watch" tasks in parallel.// This is the most commonly used task during development.// 如果在执行 `gulp` 命令时没有指定任务名,那么这个默认任务就会被调用。// 它会先运行 "build-dev" 任务,// 然后以并行的方式启动 "connect" 和 "watch" 任务。// 这个任务在整个开发期间是最为常用的。gulp.task('default',
    gulp.series('build-dev', gulp.parallel('connect', 'watch')));

典型用法

上面这个 gulpfile.js 文件的使用方法是这样的:

  • 打开终端,进入这个项目所在的目录,执行 gulp 命令。它将启动本地的 HTTP 服务,并监听文件的变动。
  • 记得保持终端窗口不被遮挡,这样你才能观察到最新的输出。
  • 在配置好 livereload 的浏览器中打开这个项目。
  • 使用编辑器或 IDE 来编辑代码(比如 HTML、CSS、JS 文件等)。
  • 在终端中观察监听任务所触发的其它任务是否产生了错误。
  • 在浏览器中观察 livereload 刷新的结果是否正确。
  • 不断重复下去。

后续

我们都很关心,Gulp 4 何时才能代替 Gulp 3 成为最新的稳定版本。在 2015 年 1 月 29 日,gulp4 的主要开发者发推说 “gulp4 本来会在 31 号问世,但最近我得了流感,估计要匿一阵子了,所以抱歉了伙计们”。时间一晃到了 2015 年 4 月 28 日,在经历了三个月的等待之后,我又问了他一次 “gulp4 的有何进展?虽然它现在已经能用了,但我们还是希望能有一个正式版出来”。遗憾的是他只回了我一句 “没有”。后来在 2015 年 5 月 19 日,Gulp 的主要开发者发推公布他们在 B 轮融资中拿到了一千万。但愿这是 Gulp 4 即将到来的好预兆。

(译注:截至本文发稿时,已经是 2016 年的夏天,Gulp 4 仍然没有发布正式版。不过实际上,我们已经在生产环境提前享受 Gulp 4 快一年了。)

总结

Gulp 是一款为 Web 开发服务的自动化工具,它十分流行,而且功能强大。看起来它的风头已经明显盖过 Grunt 了。如果你还没有用过 Gulp,赶快试试吧!

(译注:本文最初由 Leooonard 翻译,随后由 CSS魔法 整理补充而成。)


如果需要以中英对照的方式阅读,请点击 “阅读原文”。
 
CSS魔法 更多文章 快问快答(第二期) 快问快答(第一期) 图灵访谈 CSS魔法:学海无涯,而吾生有涯 StuQ 专访:CSS魔法畅聊前端人生 如何正确书写回退样式
猜您喜欢 C\/C++编程推荐学习顺序和书籍下载(V2.0) 看看你的编程语言是指环王中的哪个角色? 代码统计利器CLOC 节日快乐 创始人须以身作则