文档结构  
翻译进度:已翻译     翻译赏金:0 元 (?)    ¥ 我要打赏
参与翻译: Void_Yuan (31), 中国码农 (2)

这篇文章由 New Relic 赞助. 文章于2017年1月更新,以反映Gulp和Gulp插件的最新状况。谢谢大家对赞助商的支持,正是它们使得SitePoint成为可能!

该文章由 Giulio Mainardi 和 Tim Severien 同行评审. 感谢 SitePoint 上的所有同行评审员,他们使得 SitePoint 上的内容更加完善!

开发人员的编码时间非常宝贵。即使排除那些无聊的会议,工作中包含的很多基本任务也会消减我们的工作时间,比如:

  • 从模板和内容文档生成HTML文件
  • 压缩新图像和修改过的图像
  • 编译Sass到CSS
  • 从脚本中移除console和debugger语句
  • ES6转码到跨浏览器兼容的ES5代码
  • 代码的静态分析和验证
  • 合并、压缩CSS和JavaScript文件
  • 将文件部署到开发、缓存和生产服务器
第 1 段(可获 1.66 积分)

一旦你做出了任何修改,上述所有任务都需要重复执行一遍。往往意愿是好的,但是即使是最可靠的开发人员偶尔也会忘记压缩一两个图像。随着时间的推移,这些预生产任务会变得越来越艰巨、费时;你甚至会开始害怕不可避免的内容和模板修改。这是无聊、重复的工作。那么,将你的时间花在收益率更高的工作中是不是更好呢?

如果答案是YES的话,你可能需要一个任务流工具(任务管理器)或构建工具。

第 2 段(可获 0.93 积分)

这听起来非常复杂!

创建一个构建过程需要时间。虽然它比手动执行各项任务要复杂,但长期来看这会节省很多时间,而且可以在减少人为错误的同时节约精力。以下是一些应该遵循的最佳实践:

  • 第一步,将最有挑战的任务自动化
  • 尽量不要将你的构建过程过度复杂化;初始配置差不多1、2个小时就足够了
  • 选择某个任务流管理器并尝试一段时间,不要随意切换到其他任务流管理器

接下来讨论的其中一些工具和概念对你来说可能是全新的;但放轻松,让自己一次只专注于某一点就好了。

第 3 段(可获 1.38 积分)

可选的任务流管理器

类似 GNU Make 的构建工具已经存在了几十年,但特定于web端(前端)的任务流管理器相对来说还是个新生事物。第一个被广泛使用的前端任务流管理器是 Grunt ,它基于 Node.js 并首次提出使用JSON格式的配置文件来控制Node插件。Grunt 非常成功,但也有一些问题: 

  1. Grunt 依赖插件来完成任何基本功能,比如文件监视。
  2. Grunt 的单一插件通常会执行多个任务,这就使得根据用户需求完成定制化比较麻烦。
  3. Grunt 中任务的JSON配置比较臃肿,除了那些最基本的任务。 
  4. Grunt 的任务流程可能会执行的很慢,因为在每个任务处理过程中Grunt都要将处理结果保存为文件。

虽然有许多问题在后来的版本中得到了解决,但是 Gulp 已经领先一步出现并完成了一系列优化:

  1. 类似文件监视这种基本特性被原生支持,不需要依赖插件。
  2. Gulp 的插件基本上都被设计为只针对单一任务。
  3. Gulp 使用 JavaScript 代码来完成配置,既减小了配置文件的大小,又易于阅读、易于修改,且更加的灵活。
  4. Gulp 中的任务流执行的更快,因为它使用 Node.js streams 来在一系列串联成管道状的插件间传递中间处理数据,只在所有任务都完成以后才写文件。
第 4 段(可获 2.31 积分)

当然,Gulp 并不完美,因此出现了像 Broccoli.js, Brunch 和 webpack 之类的工作流管理器来竞相吸引开发者的关注。就在最近, 连npm都被吹捧成不失为一个简单的选择。每个构建工具都有自身的优缺点,但是 Gulp 仍然是最受喜爱的,而且超过 40% 的web开发者在使用它。

虽然 Gulp 依赖 Node.js,但是web开发领域各个方向的开发者都会发现它十分有用,要是有少许 JavaScript 背景知识的话将会更加有用。

Gulp 4怎么样呢?

这篇教程主要教你如何使用 Gulp 3, 它是写文章时最新的release版本(发布版本)。Gulp 4 也已经开发了一段时间,但目前仍然只有beta版本(测试版本)。使用或者切换到Gulp 4 是可行的,但是我还是推荐使用第三版,直到Gulp 4发布最终版本。

第 5 段(可获 1.7 积分)

步骤 1: 安装 Node.js

nodejs.org/download/ 提供Node.js 的 Windows, Mac 和 Linux版本下载,可以选择从二进制文件、包管理器、docker镜像等多种方式进行安装,网站上提供了完整的操作指南。

Windows 用户需要注意: Node.js 和 Gulp 可以在 Windows 运行,但是有些依赖于原生Linux二进制文件的插件不能正确安装或运行,比如图像压缩库。Windows 10 用户的一个可选方案是使用最新的bash命令行;它可以解决遇到的很多问题,但只有beta版本,而且可能会引入新的问题。

第 6 段(可获 1.15 积分)

安装以后,打开一个命令行窗口并输入以下命令: 

node -v

该命令用于显示Node.js的版本号。 接下来,你会不停的使用 npm ,它是 Node.js 的包管理器,用于安装第三方模块。输入以下命令检查npm的版本号:

npm -v

Linux 用户须知: Node.js 的模块可以被全局安装,这样系统的任何目录下都可以访问该模块。然而,大部分系统用户没有修改全局环境变量的权限,除非在输入npm命令之前加上sudo前缀。除此之外,还有很多 方式可以修复npm的权限问题,也可以使用类似 nvm的虚拟环境来帮助解决问题 。但我一般是通过修改shell执行的默认路径来规避权限问题,比如在基于Ubuntu/Debian的平台上可以通过如下方式:

第 7 段(可获 1.28 积分)
cd ~
mkdir .node_modules_global
npm config set prefix=$HOME/.node_modules_global
npm install npm -g

然后将下面一行添加到 ~/.bashrc 文件末尾: 

export PATH="$HOME/.node_modules_global/bin:$PATH"

并通过如下命令进行更新,从而使刚才的更改发挥作用:

source ~/.bashrc

步骤 2: 全局安装 Gulp

通过全局方式安装Gulp命令行接口,这样以后就能在任意项目目录下执行gulp命令了:

npm install gulp-cli -g

通过以下命令检测gulp是否成功安装:

gulp -v

步骤 3: 配置你的项目

Node.js 项目须知: 如果你的项目根目录下已经存在package.json配置文件,可以直接跳过这一步。

第 8 段(可获 0.74 积分)

假设你在 project1文件夹下有一个新的或已存在的项目。切换到该目录下,并通过npm初始化该项目:

cd project1
npm init

输入上述命令后,你需要回答一系列问题,可以选择输入新的值或者直接点击回车键来适用默认配置。完成之后,会生成名为 package.json 的配置文件用于保存该项目的npm相关配置信息。

Git 用户须知: Node.js 将模块安装到 node_modules 文件夹中,如果找不到该文件夹的话会创建。你需要将该文件夹的路径添加到 .gitignore 文件,这样可以确保node_modules中的所有模块不会被同步到你的版本库中。如果要在另一台电脑上部署该项目的话,可以直接使用npm install命令来重新获取这些模块。

第 9 段(可获 1.11 积分)

本文接下来的部分,我们假设你的项目根目录中包含以下子文件夹:

src 文件夹: 用于存放未处理的源文件

src子文件夹又包含以下子文件夹:

  • html – 存放HTML源文件和模板
  • images — 存放原始的未压缩图像
  • js — 存放所有的未处理的JS脚本文件
  • scss — 存放所有的未编译以.scss为后缀的Sass文件

build 文件夹: 用于存放编译后或者处理后的文件

Gulp在需要的时候会创建相应的文件或者子目录:

  • html – 存放编译后的静态HTML文件
  • images — 存放压缩后的图像
  • js — 存放唯一的合并和压缩后的JavaScript文件
  • css — 存放唯一的编译并压缩后的CSS文件
第 10 段(可获 0.93 积分)

几乎可以肯定的是,你的项目目录结构会有所不同,但是在接下来的例子中我们会依照上面的目录结构。

注意:如果你使用的是基于UNIX的系统,而且想跟随我们的教程操作一遍,那么你可以使用以下命令重建文件夹结构:

mkdir -p src/{html,images,js,scss} build/{html,images,js,css}

步骤 4: 本地安装 Gulp

使用以下命令将Gulp安装到你的项目文件夹下:

npm install gulp --save-dev

该命令将Gulp以development依赖(开发依赖)的方式安装到你的项目中,并且会同步更新根目录下 package.json的"devDependencies" 字段。教程的剩余部分,我们假设Gulp和其他所有插件都是开发依赖项。

第 11 段(可获 1.14 积分)

安装插件时可选的部署选项

当操作系统中的 NODE_ENV环境变量被设置为production的时候,Development 依赖项(开发依赖项)是不会被安装的。如果你的生产服务器是Mac/Linux系统,可以使用如下命令:

export NODE_ENV=production

Windows用户需要使用如下命令:

set NODE_ENV=production

本教程假设所有源文件都会被编译到 build 文件夹,然后再提交到 Git 仓库或者直接上传到生产服务器。然而,如果你想改变最终要部署的文件的生成方式的话(比如HTML, CSS 和 JavaScript 文件只在生产部署环境才压缩,而非开发环境),那么在生产服务器上完成构建过程会是更加可取的。在这种情况下,安装Gulp和其他所有插件的时候请使用 --save 选项。

第 12 段(可获 1.36 积分)
npm install gulp --save

该命令在package.json"dependencies"字段将Gulp设置为应用部署依赖项。以后输入 npm install 命令的时候就会安装该插件,这样不管项目部署在什么地方都能执行命令流。与此同时,可以将 build 文件夹移出你的代码仓库,因为不管在什么平台上,只要有需要都可以自行编译生成最终文件。

步骤 4: 创建Gulp配置文件

在项目根文件夹下创建一个名为 gulpfile.js 配置文件。先添加以下基本代码作为开始:

// Gulp.js configuration
var
  // modules
  gulp = require('gulp'),

  // development mode?
  devBuild = (process.env.NODE_ENV !== 'production'),

  // folders
  folder = {
    src: 'src/',
    build: 'build/'
  }
;
第 13 段(可获 0.9 积分)

代码一开始先引用Gulp模块,设置 devBuild 变量为 true 如果运行于开发模式(或者称之为非生产模式) ,随后定义源文件夹和目的文件夹的路径。

ES6 注意点: 本教程提供的都是ES5兼容的JavaScript代码。这些代码在任意版本的Gulp和Node.js(不管带不带--harmony参数启动)都能正确运行。Node 6 及以上版本支持ES6的大部分特性,所以如果你使用的是最新版本的Node.js的话,可以引入箭头函数,let,const等等关键字。

目前为止,gulpfile.js 还不会做任何事情,因为你还需要执行下述步骤:

步骤 5: 创建 Gulp 任务

Gulp 自身什么都不会做,你必须执行下列操作:

第 14 段(可获 1.23 积分)
  1. 安装Gulp 插件;
  2. 利用这些插件来编写任务,从而执行一些有用的操作。

你也可以自己编写插件,但是由于已经有3000多插件可供使用,所以一般来说我们不大可能需要自己写插件。 要搜索插件,你可以通过Gulp自己的目录gulpjs.com/plugins/ ,或者通过npm的官网 npmjs.com,或者直接在搜索引擎中输入“gulp xxx”来利用谷歌的强大功能。

Gulp 提供三个基本的与任务创建相关的API:

  • gulp.task – 通过传入名称、可选的依赖数组、执行函数来创建一个新的任务。
  • gulp.src – 设置源文件所在的文件夹。
  • gulp.dest – 设置用于存放生成文件的目标文件夹。
第 15 段(可获 1.26 积分)

插件是通过 pipe 函数串接在一起来调用的,并置于 .src 和 .dest这两个API之间。

图像处理任务

例子是最好的说明方式,所以让我们创建一个基本的任务用于压缩图像并将压缩后的图像复制到一开始设置的 build 文件夹中。由于图像压缩过程可能会很耗时,在这里我们只处理新图像和修改过的图像。接下来,我们会用到 gulp-newer 和 gulp-imagemin 这两个插件,通过命令行安装它们:

npm install gulp-newer gulp-imagemin --save-dev

现在就可以像下面这样在 gulpfile.js顶部引用这两个模块了:

// Gulp.js configuration

var
  // modules
  gulp = require('gulp'),
  newer = require('gulp-newer'),
  imagemin = require('gulp-imagemin'),
第 16 段(可获 0.91 积分)

接着,我们在 gulpfile.js文件最后将图像处理任务定义为一个函数:

// image processing
gulp.task('images', function() {
  var out = folder.build + 'images/';
  return gulp.src(folder.src + 'images/**/*')
    .pipe(newer(out))
    .pipe(imagemin({ optimizationLevel: 5 }))
    .pipe(gulp.dest(out));
});

所有的任务创建代码在语法上都是相似的。一般包括如下步骤:

  1. 创建一个新的任务,并命名为 images
  2. 定义一个带返回值的函数;
  3. 定义一个 out 文件夹用于存放处理后的文件;
  4. 通过 srcAPI设定Gulp要处理的源文件夹,其中 /**/* 通配符可以确保任意子文件夹中的图像都会被处理;
  5. 通过管道将所有源文件传递给 gulp-newer 模块,只有比对应的目标文件新的源文件可以通过管道,其他所有文件均会被过滤掉,从而不会流到下一个环节;
  6. 新的或者修改过的文件会在管道中继续通过 gulp-imagemin 插件的处理,该插件可以设置一个可选的 optimizationLevel 参数;
  7. 压缩处理后的图像会输出到由out变量指定的 Gulp dest 目录中去。
第 17 段(可获 1.33 积分)

保存 gulpfile.js 文件,并将新图片添加到项目的 src/images 文件夹下,随后通过命令行(译者注:shell需要切换到项目根目录,即gulpfile.js所在的目录)执行该命令:

gulp images

相应的,所有图片都会被执行压缩操作,你会看到如下的命令行输出:

Using file gulpfile.js
Running 'imagemin'...
Finished 'imagemin' in 5.71 ms
gulp-imagemin: image1.png (saved 48.7 kB)
gulp-imagemin: image2.jpg (saved 36.2 kB)
gulp-imagemin: image3.svg (saved 12.8 kB)

再次执行 gulp images 命令,你会发现什么都不会执行,因为没有新的图片被添加进来。

HTML 任务

现在,创建一个类似的任务用于从源HTML目录中复制文件。通过 gulp-htmlclean 插件,我们可以放心地移除HTML代码中不必要的空格和属性,从而压缩html文件:

第 18 段(可获 0.96 积分)
npm install gulp-htmlclean --save-dev

随后在 gulpfile.js文件顶部引用该插件:

var
  // modules
  gulp = require('gulp'),
  newer = require('gulp-newer'),
  imagemin = require('gulp-imagemin'),
  htmlclean = require('gulp-htmlclean'),

现在,我们便可以在 gulpfile.js文件的最后添加一个命名为 html 的任务:

// HTML processing
gulp.task('html', ['images'], function() {
  var
    out = folder.build + 'html/',
    page = gulp.src(folder.src + 'html/**/*')
      .pipe(newer(out));

  // minify production code
  if (!devBuild) {
    page = page.pipe(htmlclean());
  }

  return page.pipe(gulp.dest(out));
});
第 19 段(可获 0.23 积分)

在HTML任务中我们复用了 gulp-newer 插件,并引入了几个新的概念:

  1. [images] 参数表明 images 任务必须先于HTML任务进行 (因为HTML文件有可能会引用图像文件)。可以在该参数数组中添加任意数量的依赖任务名称,在当前任务对应的函数执行前会先完成参数数组中的所有依赖任务。
  2. 只有在 NODE_ENV 环境变量被设置为 production的时候,我们才会将HTML文件传递给gulp-htmlclean 插件去处理;因此,在开发阶段所有的HTML文件仍然是未压缩的,这样有利于在开发过程中进行调试。

保存 gulpfile.js 文件,并在命令行中执行 gulp html 任务。此时, html 和 images 两个任务都会被执行(译者注:因为html任务设置为依赖images任务)。

第 20 段(可获 1.05 积分)

JavaScript 任务

是不是很容易?接下来,我们通过一组基本模块来处理所有的 JavaScript 文件。包括以下过程:

  1. 首先,通过 gulp-deporder 插件来确保优先加载所有依赖的JS文件。该插件通过分析每个脚本文件顶部的注释来保证顺序,比如 // requires: defaults.js lib.js
  2. 使用gulp-concat插件将所有脚本文件合并到唯一的 main.js 文件;
  3. 使用gulp-strip-debug插件移除所有的 console 和 debugging 语句,并通过 gulp-uglify插件来压缩代码。(该步骤在生产部署模式才会执行)。

安装相关插件:

第 21 段(可获 1 积分)
npm install gulp-deporder gulp-concat gulp-strip-debug gulp-uglify --save-dev

在 gulpfile.js顶部引用这些插件:

var
  ...
  concat = require('gulp-concat'),
  deporder = require('gulp-deporder'),
  stripdebug = require('gulp-strip-debug'),
  uglify = require('gulp-uglify'),

接着,添加一个新的 js 任务:

// JavaScript processing
gulp.task('js', function() {

  var jsbuild = gulp.src(folder.src + 'js/**/*')
    .pipe(deporder())
    .pipe(concat('main.js'));

  if (!devBuild) {
    jsbuild = jsbuild
      .pipe(stripdebug())
      .pipe(uglify());
  }

  return jsbuild.pipe(gulp.dest(folder.build + 'js/'));

});
第 22 段(可获 0.14 积分)

保存文件,运行 gulp js 就可以见证奇迹啦!

CSS 任务

最后,我们创建一个CSS任务,使用gulp-sass插件将Sass的 .scss 文件编译为一个单独的 .css 文件。gulp-sass是 node-sass (一个绑定到LibSass C/C++ 接口的极速Sass 引擎)的复制品,以Gulp插件的方式使用 (无需安装Ruby)。 我们假设你的基本Sass文件 scss/main.scss 会负责加载其他所有的sass片段。

在CSS任务中,我们还将通过gulp-postcss插件来使用著名的PostCSS 工具。gulp-postcss会依赖其他插件,我们需要安装:

  • postcss-assets 用于资源管理,通过该插件我们可以使用 background: resolve('image.png'); 来解析文件路径,或者使用background: inline('image.png'); 来生成内嵌的数据编码图像;
  • autoprefixer 用于为CSS属性自动添加供应商前缀;
  • css-mqpacker 用于将多个对同一个媒体查询的引用打包到单独的样式规则中去;
  • cssnano 用于在生产部署模式对CSS代码进行压缩。
第 23 段(可获 1.83 积分)

首先,安装下列所有模块:

npm install gulp-sass gulp-postcss postcss-assets autoprefixer css-mqpacker cssnano --save-dev

随后在gulpfile.js顶部引用它们:

var
  ...
  sass = require('gulp-sass'),
  postcss = require('gulp-postcss'),
  assets = require('postcss-assets'),
  autoprefixer = require('autoprefixer'),
  mqpacker = require('css-mqpacker'),
  cssnano = require('cssnano'),

接下来在 gulpfile.js 文件尾部添加一个新的 css 任务。注意需要将 images 任务设置为依赖项,因为 postcss-assets 插件在构建过程中可能会引用图像文件。而且,大部分插件都支持传入参数,具体请参考各插件的说明文档。

第 24 段(可获 0.7 积分)
// CSS processing
gulp.task('css', ['images'], function() {

  var postCssOpts = [
  assets({ loadPaths: ['images/'] }),
  autoprefixer({ browsers: ['last 2 versions', '> 2%'] }),
  mqpacker
  ];

  if (!devBuild) {
    postCssOpts.push(cssnano);
  }

  return gulp.src(folder.src + 'scss/main.scss')
    .pipe(sass({
      outputStyle: 'nested',
      imagePath: 'images/',
      precision: 3,
      errLogToConsole: true
    }))
    .pipe(postcss(postCssOpts))
    .pipe(gulp.dest(folder.build + 'css/'));

});

保存文件,并通过命令行运行该任务:

第 25 段(可获 0.14 积分)
gulp css

步骤 6: 自动化任务流

在上面部分,我们都是每次手动执行一个任务;现在,我们可以在 gulpfile.js 文件中添加一个run 任务,使得在一个命令中可以执行所有的任务:

// run all tasks
gulp.task('run', ['html', 'css', 'js']);

保存文件,并在命令行中输入 gulp run 来执行所有任务。请注意,我省略了 images 任务,因为该任务已经被设置为html 和 css 任务的依赖项(译者注:所以在执行这些任务之前,images 任务会自动执行 )。

是不是觉得还是要手动执行任务仍然很繁琐? Gulp提供了另外一个内置方法 gulp.watch ,该方法可以监控源文件的任何变动,并在发生变化时执行对应的任务。该方法接收两个参数:一个需要实施监控的目录参数,一个在发生变化时需要执行任务的名称列表。最后,我们在gulpfile.js末尾添加一个 watch 任务:

第 26 段(可获 1.36 积分)
// watch for changes
gulp.task('watch', function() {

  // image changes
  gulp.watch(folder.src + 'images/**/*', ['images']);

  // html changes
  gulp.watch(folder.src + 'html/**/*', ['html']);

  // javascript changes
  gulp.watch(folder.src + 'js/**/*', ['js']);

  // css changes
  gulp.watch(folder.src + 'scss/**/*', ['css']);

});

添加完watch任务后,先不要立即执行 gulp watch 命令,我们再来添加一个default任务(译者注:默认任务,在直接输入gulp命令时执行):

// default task
gulp.task('default', ['run', 'watch']);

保存 gulpfile.js 文件并在命令行输入 gulp 。 随后,你的所有图像、HTML文件、CSS文件和JavaScript文件都会被自动处理, 而且Gulp会一直处于运行状态来实时监控所有更新操作,并在更新发生后重复执行设定的任务。 可以通过键入 Ctrl/Cmd + C 来停止监控,并返回命令行界面。

第 27 段(可获 0.66 积分)

步骤 7: 其他资源!

下面列出了你可能觉得有用的其他插件:

第 28 段(可获 1.01 积分)

gulp-util 插件中的一个实用方法是 .noop() ,该方法直接在管道中传递数据而不会执行任何任务动作。比如,使用该方法可以像下面这样更优雅的为“开发阶段”和“生产部署阶段”配置通用的任务流代码:

var gutil = require('gulp-util');

// HTML processing
gulp.task('html', ['images'], function() {
  var out = folder.src + 'html/**/*';

  return gulp.src(folder.src + 'html/**/*')
    .pipe(newer(out))
    .pipe(devBuild ? gutil.noop() : htmlclean())
    .pipe(gulp.dest(out));

});

Gulp也可以直接调用其他Node.js模块,这些模块不一定非得以Gulp插件的方式提供。

第 29 段(可获 0.55 积分)
  • browser-sync – 自动重新加载资源或者在发生改变时刷新浏览器;
  • del – 删除文件和目录 (最常见的用法是在每次命令流执行前清空 build 文件夹);

只需投入一点点时间,Gulp就可以帮你避开很多日常开发中的时间消耗。 Gulp具有如下优点:

  • 具有各种丰富的专用插件
  • 配置文件采用管道方式,易于阅读和遵循
  • gulpfile.js 稍加修改便可以直接重用在其他项目中
  • 减少页面总量,从而提高网站性能
  • 简化部署过程

可用链接:

第 30 段(可获 1.08 积分)

将上述过程应用到一个简单的网站便能减少超过50%的页面大小。你可以通过 页面大小分析工具 或者通过 New Relic(该机构提供一系列复杂的应用程序性能检测工具)提供的服务测试得到自己的数据。

Gulp可以彻底改变你的工作流,我希望你会觉得该教程有用并考虑将Gulp添加到你的web开发过程中去。我很乐意在下面的评论中听到你的想法。

第 31 段(可获 0.94 积分)

文章评论

访客
非常感谢作者的分享,之前一直不太熟悉,这下基本完全明了