快速掌握Gulp自动化构建工具

Gulp是什么?

Gulp是一个自动化构建工具,类似Webpack,目的是把开发环境的代码转换为生产环境的代码,比如ES6转ES5、Saas转CSS、文件压缩等。

Gulp的整体设计思路是通过文件读写和一系列Stream插件实现文件内容处理,完成构建工作。完整的构建工作可以拆分为多个构建步骤,每一个构建步骤称之为任务,一个大型的项目构建由多个任务组合完成。

Gulp基本使用

1.Gulp执行依赖Node环境,需要安装Node
2.安装命令行工具gulp-cli
3.安装核心依赖库gulp
4.创建gulpfile.js,在其中书写任务代码
5.导出要执行的任务
6.使用命令行工具执行任务

下面是代码演示:

# 安装node环境
# 安装命令行工具
npm install -g gulp-cli@2.3.0 
# 安装核心依赖库
npm install -D gulp@4.0.2
# 创建gulpfile.js
touch gulpfile.js
# 书写并导出任务,见下文
# 执行任务
gulp xxxx # 导出任务函数名称/default 

在gulpfile.js中书写并导出任务:

// vim gulpfile.js

const { src, dest } = require("gulp");

// 任务函数
function copy() {return src("src/index.html").pipe(dest("dist/"));
}

// 导出任务
module.exports = {copy,
};

// gulp copy 执行任务 

Gulp核心概念

基于Stream流的任务创建

一个任务就是一个函数,创建任务就是创建一个函数,然后将要执行的函数导出即可。

任务函数的内部通常由一个一个的流处理器构成的流处理管道,起点和终点是文件读写,中间是各种流处理插件,流处理器之间则通过stream.pipe连接,后面我们重点看看这几个部分。

文件读写流

gulp.src / gulp.dest

Gulp本身是非常简单的,在流处理器方面只提供了gulp.src和gulp.dest两个函数用于文件读写,中间的处理器全部由外部插件提供,这些插件全部按照Gulp提供的规范编写,可以无缝嵌入Gulp流处理过程。这种基于组合的设计思路使得Gulp本身非常精简,同时也让Gulp的生态越来越丰富。

我个人非常喜欢这种轻量化的设计思路,只需要提供一套规范和主流程框架就可以实现各种功能,同时还能保证项目的健壮性、可维护性和可扩展性。

gulp.src和gulp.dest是一个读写流,继承自stream.Duplex,为什么是读写流而不是一边只读,一边只写呢?因为src和dest不仅用于两端,还会用在中间部分,比如先读一个文件处理一下,然后再读一个文件进行合并压缩等等。

这两个函数的使用也比较简单,最常用的就是像上文copy函数那样开头读一个文件,结尾将文件写入一个目录。

function copy() {return src("src/index.html").pipe(dest("dist/"));
} 

src接收一个路径字符串或者路径数组,用于读取一个或者多个文件,而dest则接收一个目录路径字符串,用于将内容写入文件。

src("src/index.html")
src(["src/index.html", "src/index.css"])
dest("dist/") 

值得注意的是,这里的路径字符串是Glob路径匹配符而不是普通的字符串,通过Glob的方式可以方便的匹配任意层级、任意格式的文件。

Glob路径匹配符

Glob用于匹配文件路径,由普通字符、特殊字符和分隔符三个部分组成,即基本用法如下:

  • 分隔符只能是"/"
  • 普通字符就是正常的文本,没有特殊含义
  • 特殊字符包括:* *: 匹配单级目录下任意数量的字符* **: 匹配任意级目录下任意数量的字符* !: 取反,必须在数组中跟在一个正向的glob后面* {}: 取或,同时匹配多个模式,如{ab,cd}

接下来我们对特殊字符举例说明:

首先*通常用来批量匹配文件,比如*.js,*.png等等,或者dist/*匹配所有文件。

其次**通常用来匹配跨越多级目录,比如images/**/*.png,如果直接写images/**.png,那大概率只能匹配images单级目录下面的png文件,因为子目录的名字里没有.png后缀,**虽然可以跨层级,但也是一级一级往下匹配的,如果中间一级不能匹配上,那就会不会继续向下匹配了。

!通常用来表示在批量匹配之后排除其中的个别文件,所以需要跟在正向匹配后面,比如["src/*.js", "!src/abc.js"]

{}通常用来匹配多个文件后缀,比如*.{png,jpg},注意{png,jpg}的逗号两边不能有空格。

好了,理解Glob之后那文件读写就变得非常轻松了,接下来就看看如何处理这些流。

流处理插件

在Gulp中,文件流的处理是通过各种流处理插件完成的,这些插件继承自stream.Transform,本质就是一个转换流。不同的插件之间通过stream.pipe()相连,由此构成了一个处理流水线,实现整个构建任务。

Gulp提供了一个插件查询的网址:gulpjs.com/plugins ,我们要做的就是下载这些插件,然后把他放置在需要的pipe()之中进行执行。

src('src/index.html').pipe(插件1).pipe(插件2).pipe(dest("dist/")) 

对于前端开发来说,常用的插件如下:

1.HTML相关:* gulp-htmlmin: 压缩HTML文件* gulp-file-include:引入HTML代码片段
2.CSS相关:* gulp-sass/gulp-less: SAAS、LESS转CSS* gulp-autoprefixer: 自动添加厂商前缀* gulp-cssmin: 压缩CSS文件
3.JS相关:* gulp-babel: ES6转ES5* gulp-uglify: 压缩JS文件
4.其它:* gulp-rename: 文件重命名* gulp-concat: 文件合并* gulp-webserver: 搭建本地服务器* webpack-stream: 在gulp中使用webpack

通过组合任务实现Gulp执行

大型项目的构建往往要拆分为多个构建任务,而任务就是gulp的执行单元。任务之间可以通过组合形成新的任务,这种组合方式可以实现任意复杂度的构建流程。

单个任务

上文我们介绍了一个任务的本质就是pipe连接的stream处理流水线,这边再补充几点关于任务的知识:

1.一个任务就是一个函数。
2.只有导出的任务函数才能被执行,执行命令为gulp [任务函数名称]
3.通过exports.default导出的任务称为默认任务,可以通过gulp直接执行。
4.任务完成后需要通知Gulp,否则控制台就是报错说没有收到任务结束的通知。对于组合任务来说,通知尤其重要,如果多个任务之间是线性执行的,那么只有当收到前一个任务完成的通知之后,Gulp才会执行下一个任务。任务结束通知有三种方式:* 任务函数可以接收一个cb参数,调用cb回调进行通知* 任务函数返回一个Promise* 任务函数返回一个Stream

接下来我们演示一下这三种任务结束通知方式:

// 执行回调函数
function start(cb) {console.log("task start");cb();
}

// 返回Promise
function clean() {return new Promise((resolve, reject) => {setTimeout(() => {console.log("dir clean finished");resolve();}, 1000);});
}

// 返回Stream
function copy() {return src("src/index.html").pipe(dest("dist/"));
} 

组合任务

当我们需要将多个任务按照顺序进行组合,依次执行或者并行执行时就可以使用组合任务。Gulp提供了series和parallel两个API用于创建组合任务。

series和parallel都会返回一个新的任务,我们可以像使用普通任务那样使用组合任务,两者的基本使用方法如下:

const {series, parallel} = require("gulp");

series(task1, task2, ...);
parallel(task1, task2, ...); 

series是线性执行,其中的任务会一个接一个的执行,只有当前面的任务通知Gulp执行完成后,后面的任务才会执行。

paralle是并行执行,任务之间没有先后关系,这种执行方式可以提高整体的构建效率。

文件监听

在开发过程中,我们希望构建系统能够监听文件的变化自动构建,实现页面的热更新。为此,Gulp提供了一个watchAPI实现文件变化的监听,当文件发生变化后,Gulp将自动执行注册的任务。

const { watch } = require("gulp");

function watchJS() {watch('src/index.js', task1);
}

exports.default = watchJS; 

一个完整的案例

最后,我们用一个完整的案例把上面的知识串起来。在这个案例中,我们将监听js模块文件的变化,实现如下几个构建动作:

1.清理dist目录
2.使用gulp-babel将ES6转为ES5
3.使用gulp-concat将多个模块组合为单个文件
4.使用gulp-uglify进行JS文件压缩
5.使用gulp-rename重命名最终生成的JS文件

让我们开始吧!

在src目录下有两个JS模块文件moduleA.js,moduleB.js,我们的目的就是把他打包为module.min.js。

// moduleA.js

const animal = "dog";
// 跑得更快
function run() {console.log("run faster!");
}

// moduleB.js

const animal = "bird";
// 飞得更高
function fly() {console.log("run higher!");
} 

1.安装所有需要的插件

npm install -D gulp-babel@8.0.0 @babel/core@7.14.8 @babel/preset-env@7.14.8
npm install -D gulp-uglify@3.0.2 gulp-rename@2.0.0 gulp-concat@2.6.1
# 这个不是插件,用于删除文件
npm install -D del@6.0.0 

2.编写gulpfile.js

const { src, dest, series, watch } = require("gulp");
const babel = require("gulp-babel");
const concat = require("gulp-concat");
const uglify = require("gulp-uglify");
const rename = require("gulp-rename");
const del = require("del");

function clean() {// 返回Promisereturn del("dist");
}

function build() {return src("src/module*.js").pipe(babel({ presets: ["@babel/env"] })).pipe(concat("module.js")).pipe(uglify()).pipe(rename({ extname: ".min.js" })).pipe(dest("dist/"));
}

function watchJS() {watch("src/module*.js", series(clean, build));
}

exports.default = watchJS;

// 命令行执行gulp 

最终我们构建得到的module.min.js内容为:

"use strict";var animal="dog";function run(){console.log("run faster!")}animal="bird";function fly(){console.log("run higher!")} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值