盒子
文章目录
  1. Gulp 概述
  2. 使用 Gulp
    1. 安装
    2. Gulpfile.js
  3. 基本概念与原理
    1. 认识 Glob
    2. 认识 Vinyl
    3. 认识 Orchestrator
  4. Gulp 核心 API
    1. gulp.src
    2. gulp.dest
    3. gulp.task
    4. gulp.watch
  5. 常用插件

Gulp 基础与原理

Gulp 概述

Gulp 是基于 NodeJS 的项目,一个用作自动化构建的工具,业界一般用来建造前端的工作流。

它的核心原理其实很简单,最主要是通过各种 Transform Stream 来实现文件的处理,然后再进行输出。Transform Streams 是 NodeJS Stream 的一种,是可读又可写的,它会对传给它的对象做一些转换的操作。

文件输入 → Gulp 插件处理 → 文件输出

原则上,gulp 可以针对文件做任何有趣、有创造力事情。
而自动化构建,只是大家主要比较喜欢使用的方向。

Gulp 的特点:

  • 自动化 - Gulp 为你的工作流而服务,自动运行那些费事费力任务。
  • 平台透明 - Gulp 被集成到各种 IDE 中,并且除了 NodeJS 之外,其他如 PHP、.NET、Java 平台都可以使用 Gulp。
  • 强大生态系统 - 你可以使用 npm 上 2000+ 的插件来构造你的工作流。
  • 简单 - Gulp 只提供几个 API,这可以很快地学习和上手。

使用 Gulp

安装

1
2
$ npm install gulp-cli -g // 全局安装 Gulp 命令行工具
$ npm install gulp -D // 在项目中,作为 devDependencies 依赖安装 gulp

Gulpfile.js

在使用 CLI 工具的时候,会执行该文件,它是一个可执行的 NodeJS 文件。原理上,你可以在里面运行任何 NodeJS 代码,然后通过调用 gulp 提供的 API,来执行 gulp 任务。
gulpfile.js 文件一般都会放在项目的根目录中。

一个使用 gulp-babel 插件来支持 es2015 语法的案例:

1
2
3
4
5
6
7
8
9
10
const gulp = require('gulp');
const babel = require('gulp-babel');
gulp.task('default', () => {
gulp.src('src/app.js')
.pipe(babel({
presets: ['es2015']
}))
.pipe(gulp.dest('dist'));
});

基本概念与原理

了解这些概念,对于了解 Gulp 的工作原理,和 API 的使用是很有帮助的。

认识 Glob

Glob 是一种用来匹配路径与文件的模式。有点类似于正则表达式,但是语法又有点差异。
这种模式,被广泛用于命令行、Shell 等场景,大家熟悉的 .gitignore 文件也是使用这种模式。

各大语言都有对于 Glob 的实现,例如 Go 和 PHP 的 Glob 函数,Python 中的 glob 模块。
而 NodeJS 的实现是 minimatch, 而在 Gulp 源码中,就用了对 minimatch 进行封装的 node-glob 模块。
Gulp 的 API gulp.watchgulp.src 都有用到 Glob 来匹配对应的路径和文件。
下面是部分语法:

  • *
    匹配该路径段中 0 个或多个任意字符,
    如:js/*.js, 匹配 js 目录下的所有 js 文件
  • ?
    匹配该路径段中 1 个任意字符,
    如:js/?.js,匹配 js 目录下所有名字只有 1 个字的 js
  • [...]
    匹配该路径段中在指定范围内字符,
    如:js/a[0-3].js,匹配 js 目录下 a 开头,第二个字符为 0-3 之间( 包括0和3 )的 js( a03.js不能被匹配到 )

  • !(pattern|pattern|pattern)
    匹配除所给出的模型以外的情况,
    如:js/!(a|b).js,匹配 js 目录下名字中不包含 a ,也不包含 b 的所有文件.

  • ?(pattern|pattern|pattern)
    匹配所给出的模型中的 0 个或任意 1 个,
    如:js/?(a|a2|b).js, 匹配 js 目录下 a.js , a2.js , b.js

  • +(pattern|pattern|pattern)
    匹配所给出的模型中的 1 个或者多个,
    如:js/+(a|a1|b).js, 匹配 js 目录下 a.js , a1.js , b.js , 或者 a, a1, b 这几个字符的组合的 js , 比如 ab.js

  • *(pattern|pattern|pattern)
    匹配所给出的模型中的 0 个或多个或任意个的组合.
    如:js/*(a|a1|b).js,匹配 js 目录下 a.js, a1.js, b.js 或者 a, a1, b这几个字符的组合的 js , 比如 ab.js

  • @(pattern|pat*|pat?erN)
    匹配所给出的模型中的任意 1 个,
    如:js/@(a|a1|b), 匹配 js 目录下的 a.js, a1.js, b.js

  • **
    * 一样可以匹配任何内容,但 **不仅匹配路径中的某一段,而且可以匹配 a/b/c 这样带有 / 的内容,所以,它还可以匹配子文件夹下的文件.
    如:js/**/*.js,匹配 js 目录下及子文件夹中所有的 js 文件。

更多 Glob 的知识和语法,可以参考:
Glob - Wiki
Glob Primer

认识 Vinyl

Vinyl 是 Gulp 自创的一种用来描述一个虚拟文件的类,其中主要包括文件的内容和文件的路径两大信息。vinyl 模块,只是提供了一个类,而实现却交由 vinyl-fs

Vinyl-fs,它主要的工作是接受 glob 模式的参数,然后读取匹配的文件。然后利用 Vinyl 制作一个 Transform Stream,称为 Vinyl Stream 对象,并返回。

在 Gulp 中的 API gulp.srcgulp.watchgulp.dest 都返回一个 Vinyl Stream 实例对象。Vinyl Stream 实例之间可以通过管道( vinyl1.pipe(vinyl2) )的形式来互相传输数据。

从 Gulp 的 源码 中也能看出,这三个 API 都是由 vinyl-fs 提供全部的实现。

再一点是,从这两个模块的实现来看,Gulp 是把文件内容以 Buffer 的形式读到内存中,然后再进行处理的。

认识 Orchestrator

Orchestartor,为 gulp.task 提供了全部实现,这可以从 源码 中看出。
它为 Gulp 提供了任务相关的功能,包括任务注册、任务执行以及相对应的任务进度、错误监控等功能。

Orchestartor 模块,只提供了一个 Orchestartor 类,该类的实例维护着一个 tasks 数组,该数组的内容就是一个我们使用 gulp.task 时注册的函数列表,以及函数的依赖和名字。
通过 源码 中,可以看到 tasks 的数据结构:

1
2
3
4
5
6
7
...
this.tasks[name] = {
fn: fn, // 任务的函数体
dep: dep, // 任务所依赖的其他任务名称
name: name // 该任务的名称
};
...

Gulp 核心 API

  • gulp.src:获取文件
  • gulp.dest:写入文件
  • gulp.tasks:注册任务
  • gulp.watch:监控文件的改动

gulp.src

gulp.src( globs [, options] )

接收一个 globs 模式的对象,可以是 Array 或者 String,返回一个 Vinyl Stream 实例。
而 options 有下面的值:

  • buffer - Boolean, 控制 file.contents 是返回 buffer 还是 stream。
  • read - Boolean,控制是否读取文件,如果 false,则 file.contentsnull
  • base - String,控制 glob 的 base,默认值是 glob 所有表达式的前置,例如 client/js/**/*.js, base 值就为 client/js/。而 glob 在保存输出路径的时候,取的是 base 之后的路径。所以可以通过该值,来进行输出路径的改写。

gulp.dest

gulp.dest( path [, options] )

接收输出路径,返回一个 Vinyl Stream 实例。
而 options 有以下的值:

  • cwd - String, 默认值 process.pwd(),输出目录的 cwd 参数,只在所给的输出目录是相对路径时候有效。
  • mode - String,八进制权限字符,用以定义所有在输出目录中所创建的目录的权限。

gulp.task

gulp.task( name [, deps ], fn )

定义一个使用 Orchestrator 实现的任务(task)。
参数的描述如下:

  • name - 任务名称
  • deps - 是当前定义的任务需要依赖的其他任务,为一个数组。当前定义的任务会在所有依赖的任务执行完毕后才开始执行。如果没有依赖,则可省略这个参数
  • fn - 为任务函数,我们把任务要执行的代码都写在里面。该参数也是可选的。

gulp.watch

gulp.watch( glob [, opts ], tasks )
or
gulp.watch( glob [, opts, cb ] )

用来监视文件的变化,当文件发生变化后,我们可以利用它来执行相应的任务。
各参数的描述如下:

  • glob - 为要监视的文件 Glob 匹配模式。
  • opts - 为一个可选的配置对象。
  • tasks - 为文件变化后要执行的任务,为一个数组

常用插件

更多插件,可以搜索官方插件库