Gulp 构建入门

Dec 26, 2018 阅读(1063)

标签: Gulp

Gulp 介绍

Gulp 是用 JavaScript 编写 、 运行在 Node.js 上的构建工具 。 因为 Gulp 是一个 JavaScript 程序,而且它的指令也是用 JavaScript 编写的,所以 Gulp 非常贴近 JavaScript 开发者的日常工作环境 。 因此, Gulp 就成了前端开发者自动化处理日常任务的首选工具 。

 

配置Gulp

一般而言 ,构建工具由两部分组成:执行构建任务的工具和包含构建指令的配置文件 。 Gulp 也不例外 。 Gulp 执行构建任务的工具就是 Gulp ,而 Gulp 的构建配置文件叫作 Gulpfile 。

完整的 Gulp 安装流程包括以下几个部分 :

  • Gulp CLI : 启动构建工具的命令行接口 。

  • 本地 Gulp : 构建时实际运行的程序 。

  • Gulpfile : 告诉 Gulp 如何构建软件的指令文件 。

  • Gulp 插件: 众多用来合井、修改、组装文件 的插件 。

Gulp 命令行接口

Node.js 模块可以全局安装,所以用户可以像使用命令行工具一样使用  Node.js 模块 。 Node.js 模块也可以安装到本地目录,作为项目依赖的库使用 。

1545311578526_039

Gulp 也是如此,除了有一点不同,那就是 Gulp 有两个独立的包 。一个是 Gulp 的命令行接口(以下简称 Gulp CLI ),它需要全局安装,所以 Gulp 能够在终端 、 bash 和命令提示符中直接运行 。 另一个是本地的 Gulp ,它是真正的 Gulp 运行时 。 负责处理所有任务,并且提供所有的 API 。 Gulp CLI 是本地 Gulp 的全局的人口,它负责把所有参数转发到本地 Gulp ,还有显示项目里安装的本地 Gulp 的版本 。 后面的就都是本地 Gulp的任务了,主要由它来处理构建 。

安装 Gulp CLI

# 检查是否正确安装了 Node.js:
smqk@lenovo:~$ node --version
程序“node”尚未安装。 您可以使用以下命令安装:
sudo apt install nodejs-legacy
# 安装 Node.js
smqk@lenovo:~$ sudo apt install nodejs-legacy
[sudo] smqk 的密码: 
正在读取软件包列表... 完成
正在分析软件包的依赖关系树       
正在读取状态信息... 完成       
下列【新】软件包将被安装:
  nodejs-legacy
升级了 0 个软件包,新安装了 1 个软件包,要卸载 0 个软件包,有 58 个软件包未被升级。
需要下载 27.7 kB 的归档。
解压缩后会消耗 81.9 kB 的额外空间。
获取:1 http://cn.archive.ubuntu.com/ubuntu xenial/universe amd64 nodejs-legacy all 4.2.6~dfsg-1ubuntu4 [27.7 kB]
已下载 27.7 kB,耗时 5秒 (5,005 B/s)     
正在选中未选择的软件包 nodejs-legacy。
(正在读取数据库 ... 系统当前共安装有 274764 个文件和目录。)
正准备解包 .../nodejs-legacy_4.2.6~dfsg-1ubuntu4_all.deb  ...
正在解包 nodejs-legacy (4.2.6~dfsg-1ubuntu4) ...
正在处理用于 man-db (2.7.5-1) 的触发器 ...
正在设置 nodejs-legacy (4.2.6~dfsg-1ubuntu4) ...
# 查看 node.js 版本
smqk@lenovo:~$ node --version
v4.2.6
# 查看 npm 版本
smqk@lenovo:~$ npm --version
3.5.2
# 安装 Gulp CLI, -g 参数表示全局安装
smqk@lenovo:~$ sudo npm install -g gulp-cli
loadDep:urix → 304  ███████████████████████████████████████████████████████░░░░
/usr/local/bin/gulp -> /usr/local/lib/node_modules/gulp-cli/bin/gulp.js
/usr/local/lib
└── gulp-cli@2.0.1
# 查看 gulp 版本
smqk@lenovo:~$ gulp --version
[21:28:34] CLI version 2.0.1
smqk@lenovo:~$

 

安装本地 Gulp

本地 Gulp 有两个主要作用:一个是加载和运行 Gulpfile 中的构建指令,另一个是暴露 API 供 Gulpfile 使用 。 本地 Gulp 是真正运行构建任务的程序,全局 Gulp 只是用来启动各个项目中的本地 Gulp ,图 2-4 展示了这种关系 。

1545312647767_005

本地 Gulp 位于本地 JavaScript 项目的 node_modules 目录下,它包含了 Gulpfile 所需的所有函数和 API

要安装本地 Gulp,,首先打开命令行,并进入到项目的根目录 。 然后,通过下面的命令,把整个项目初始化成一个 Node 模块。

npm init

输入命令后出现的是一个简短的安装提示,其中会询问一些项目信息 。不过如果你不打算把新模块发布到 NPM 仓库( registry )中(或者至少现在不想),你可以把所有值都设为默认值 。完成之后,你就会在目录中看到一个名为 package.json 的新文件,它保存了应用的所有 Node 模块信息和版本 。 这个文件是每个 Node 项目的核心,而且有的插件也会从这个文件中获取已安装的模块信息 。package.json 文件保存了所有项目依赖 Node 模块的信息 。 它把依赖分为运行时依赖(项目正确运行所必需的模块)和开发依赖(开发项目时需要的模块)。因为构建工具属于开发依赖,所以要用下面的命令安装本地 Gulp:

 npm install --save-dev gulp

Gulp 安装好了, save- dev 参数的作用是把安装的 Gulp 的版本正确保存在 packge.json 文件中:

{
  "name": "gulp-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

 

创建 Gulp file

Gulp 的“ Hello World ” task

Gulpfile 是包含了所有安装指令的 JavaScript 文件,安装指令包含一 系列被打包成task (任务) 的命令 。 在 Gulp 中, task 是一个 JavaScript 函数,其中包含了所有要运行的命令 。task 可以 是任何函数,但是只有要用 Gulp API 中的 gulp.task 方法来定义这个函数,它才是一个 task 。

1545365367018_001

gulp.task 方法的作用只有 一个: 给纯 JavaScript 构成的 task 函数赋予一个名字 。这个名字会把 task 函数添加到 Gulp 的运行时的命名空间中 。 这样一来, Gulp 就能知道这个函数的存在,并且能根据引用来执行它了 。也就是说gulp.task 可以提供一个在命令行中直接调用这个函数的接口 。

在包含 package.json 和 node_modules 的目录中创建一个新的空文件,名称叫作 Gulpfile.js。

// 在 Gulpfile 中引用本地 Gulp
var gulp = require("gulp");

// 定义一个名叫 test 的新 task
gulp.task("test",function(){
	// 命令行打印“ Hello World !”
	console.log("Hello World !");
});

Gulp 和 Gulpfile 之间有一种独特的关系: Gulp 需要 Gulpfile 才能知道有哪些 task可以运行; Gulpfile 需要 Gulp 才能调用 Gulp API 。

1545387161757_020

使用 gulp.task 这个 API 就能把一个函数,变成一个 task。 在上面示例代码中创建的“ Hello World !” task 就是一个名为 test 的 task。 这个名字会保存在 Gulp 的运行环境中 。 你可以通过下面的命令,在调用 Gulp 命令时,直接执行这个 task:

smqk@lenovo:~/git/test/gulp-demo$ gulp test
[13:33:40] Using gulpfile ~/git/test/gulp-demo/Gulpfile.js
[13:33:40] Starting 'test'...
Hello World !
[13:33:40] The following tasks did not complete: test
[13:33:40] Did you forget to signal async completion?
smqk@lenovo:~/git/test/gulp-demo$

执行上面这个 task 的具体流程如下:

  • 全局 Gulp CLI 加载本地 Gulp 。

  • 本地 Gulp 加载 Gulp file 。

  • Gulpfile 加载本地 Gulp ,然后定义一个名为 test 的 task 。

  • 本地 Gulp 接收到一个命令行参数,也就是要执行的 task 的名字 。

  • 本地 Gulp 发现确实有一个 task 叫这个名 字,于是执行 了这个 task 所绑定的函数 。

 

流的使用

通过 Gulp 你可以读取文件内容,然后把它转换成特定的形式,最后把众多 Java­Script 文件合并成一个文件 。 Gulp API 提供了一些方法来读写、转换文件,而在所有这些方法背后,其实都使用了流( stream ) 。

流在计算机领域并不是一个新概念,它起源于 1960 年代的早期的 Unix ,流是从一个源( source )流向一个终点( destination )的数据的序列 。 源的形式有很多种类:文件、计算机内存、输入设备,比如键盘和鼠标 。 当一个流打开时,数据就会一块一块地从源头流出,然后被预先定义的过程处理 。 如果数据源来自于文件,那么每个字符或字节都会被依次读取;如果数据来源于键盘,那么每次按下的键都会通过流传输数据 。 相比于一 次性加载所有数据,流最大的优点是:理论上,输入数据可以是无限的,而且没有任何限制 。 对于键盘而言,这一点就很有意义。哪有人会关闭控制电脑的输入流呢?输入流也被称之为可读流( readable stream ),意思是从数据源读取数据 。 相对应的,还有还有向外输出的流 。 它可以是文件,也可以是内存里的数据,还可以是输出设备,比如命令行 、 打印机、显示器 。 这些流也被称之为可写流( writable stream), 意思是它们将来自流的数据保存起来 。

 

Gulp 中的可读流和可写流

在 Gulp 中,你可以使用 gulp .src 方法来创建文件的可读流,它可以用来选择你想在 task 中处理的文件 。 不过由于你可能要处理很多不同的文件,而且很可能不知道文件具体叫什么名字,所以你可以用 glob 来定义匹配规则( selection pattern ) 。 与可读流gulp.src 相对的是可写流 gulp.dest ,它用来定义文件保存的位置 。 gulp.dest 接受一个字符串参数,用来表示文件目录 。 这个目录是相对于 Gulpfile 所处的目录的 。 如果这个目录不存在, Gulp 会创建一个。

// 在 Gulpfile 中引用本地 Gulp
var gulp = require("gulp");

// 定义一个名为 copy 的新task
gulp.task('copy', function(){
	//  匹配了 app/scripts 目录及其子目录下的所有后缀名是 .js的文件,然后,通过 pipe 方法把流的内容传入其他函数,gulp.dest 函数将流保存到了 dist 目录下
	return gulp.src("app/scripts/**/*.js")
		.pipe(gulp.dest("dest"));
});

 

使用 Gulp 插件处理 task

转换数据

流所擅长的可不仅仅是在不同的输入输出之间传递数据 。 当流打开时,开发者可以在数据传递到终点之前转换数据 ,比如把所有小写字母变成大写字母之类的 。

1545388705100_033

要修改数据,就要 在输入和输出之间添加转换方法( transformation ) 。 在这个例子中,来自各个不同的源的数据被传入到了 toUpperCase 方法中 。 这个方法会把所有小写字母转换为大写字母 。 转换方法定义一次,就能在不同的流中复用 。

在 Gulp 中,转换方法是通过插件来实现的 。 Gulp 的生态包含了超过 1500 多个插件,能让你用各种手段转换数据 。 下面让我们来看看如何通过 JavaScript 界最常见的转换步骤 一 Uglify (混淆),来转换 JavaScript 文件 。

Uglify 是用 JavaScript 写 的用来压缩文件的库 。 它能删除所有多余的空格,还能在不影响 API 的前提下尽可能缩短变量和函数名,甚至还采用了本书中介绍的各种优化 JavaScript 代码的方法(比如把 true 写成 !0 ,因为这样能节省两个字节) 。 这样一来,代码就会变得非常小,从而能够更快地被解析和传输 。 以流行的 jQuery 为例,经过压缩后, jQuery 的体积能从250KB 缩小到 90阻,缩小了将近 三分之一。 不过一旦代码经过混淆后,人类就很难阅读了,这也是为什么它叫做 Uglify 。 Uglify 有一个 Gulp 插件,可以用下面的命令安装:

#语法格式: npm install --save-dev <plugin-name> 
#这行命令能够安装 Uglify 的 Gulp 插件,并且把对应的记录保存到 package.json 中 。 
npm install --save-dev gulp-uglify

 

改变文件结构 — 内容合并

在加载 JavaScript 应用时,发的请求数量越少越好,所以我们要尽量把所有的 JavaScript 文件合并成一个文件 。 这个 task 通过 concatenation (合并)完成, concatenation 能够把很多文件的内容合并到一个文件中 。 这个新文件拥有一个新的文件名和所有旧文件的内容 。

gulp-concat 模块可以预先通过 npm install --save-dev gulp-concat 来安装。 第 1 步把所有的 JavaScript 文件内容传入到 concat 步骤中 。 concat 插件需要一个参数 : 新文件的文件名 。 concat 插件会用这个新名字创建一个新的虚拟文件对象 。 这个虚拟文件对象的内容就是之前流中的所有的文件的内容 。 详情参见如下 Gulpfile.js 代码:

// 在 Gulpfile 中引用本地 Gulp
var gulp = require("gulp");
// 引入 gulp-concat 包来处理合并过程
var concat = require("gulp-concat");

gulp.task('scripts', function(){
	return gulp.src("app/scripts/**/*.js") 
		.pipe(concat("bundle.js"))	// 创建可读流之后、创建可写流之前使用该插件
		.pipe(gulp.dest("dest"));
});

 

链式调用插件实例

完整的 Gulpfile.js

var gulp 		= require("gulp");
var jshint 		= require("gulp-jshint");
var uglify 		= require("gulp-uglify");
var concat 		= require("gulp-concat");
var less 		= require("gulp-less");
var minifyCSS 	= require("gulp-cssnano");
var concat 		= require("gulp-concat");
var prefix 		= require("gulp-autoprefixer");

gulp.task('scripts', function(){
	return gulp.src("app/scripts/**/*.js")
    	.pipe(concat("main.min.js"))
		.pipe(uglify())				//对流内容加以混淆
		.pipe(gulp.dest("dest/scripts"));
});

gulp.task('styles', function(){
	return gulp.src("app/styles/main.less")
    	.pipe(less())
    	.pipe(minifyCSS())
		.pipe(prefix())
		.pipe(gulp.dest("dest/styles"));
});

gulp.task('test', function(){
	return gulp.src("app/scripts/**/*.js","!app/scripts/vendor/**/*.js")
    	.pipe(jshint())
    	.pipe(jshint.reporter("default"))
		.pipe(jshint.reporter("fail"));
});

用 npm install --save-dev

 

总结

介绍了 Gulp 的人门知识 :

  • Gulp 的运行环境: Node.js 自带 一个包管理器 NPM 。 NPM 可以全局安装 node.js 模块作为命令行工具,也可以在本地安装模块作为库来使用 。 Gulp 既有 全局安装的命令行接口,也有在每个项目本地安装的库 。

  • Gulpfile 是用 JavaScript 编 写的构建指令,它使用本 地 Gulp 来调用 API 。 你可以在 Gulpfile 中引用 Node 模块,使用它们 。

  • Gulp 的 gulp.taskAPI 使函数能够在命令行中被调用 。 执行 gulp

  • gulp.src 和 gulp . dest 能够创建可读流和可 写流,用来 把文件从文件系统里的一个地方复制到另 一个地方 。

  • 插件,比如 Concat 和 Uglify ,可以用来转换 JavaScript 文件 。

  • 链式调用插件可以创造出更复杂的程序,比如一个既能压缩又能合并的script task 。 又或者是一个能编译、压缩 css 文件,并且自动添加浏览器前缀的style task 。

  • 使用 JSHint 进行代码风格检查可以确保代码质量 。

 

 

 

MongoDB学习园