对比webpack,你更应该先掌握gulp【10分钟教你彻底掌握gulp】

前言

可能很多人对于 gulp 都相对陌生,特别是 vuereact 出现以后,渐渐淡出了做业务前端人员的视野,14到16年的时候应该是它最巅峰的时候,真正的是出道即巅峰,取代了当时最火的 grunt 成为了前端构建的主流工具,就连某度都忍不住来瓜分一下流量,出了个 fis (不过按照烂尾的惯例来看,基本会属于后继无人的状态,所以没有真正去用在生产项目中过),而且当时 webpack 虽然已经出现,但完全不能跟 gulp 抗衡,直到 vuereactspa 项目出现,才让 webpack 取而代之, gulp 也逐渐退出幕前,转战幕后,去做了它更擅长的事情: 前端开发流程规范管理

现在我们在各种组件库,像 antdelement-uivant 等比较人们的组件库,或者其他一些前端工程中都能看到它的身影,只不过它不再介入到业务的实际生产开发中了,所以对业务开发人员来说是不太能感知到它的存在了。

gulp和webpack的区别

首先,可能很多人面试过程中都会被问到这个问题。我说一说自己的理解:

gulp webpack
强调的是规范前端开发的流程 是一个前端模块化方案
是一个基于流的自动化构建工具,不包括模块化的功能,通过配置一系列的task,例如文件压缩合并、雪碧图、启动server、版本控制等,然后定义执行顺序来让gulp执行task,从而构建前端项目的流程 是一个自动化模块打包工具,把开发中的所有资源(图片、js文件、css文件等)都看成模块,通过loader(加载器)和plugins(插件)对资源进行处理,划分成不同的模块,需要哪个加载哪个,实现按需加载的功能,入口引入的更多是js文件

webpack 刚面世的时候, webpackgulp 中也有一个插件( gulp-webpack )作为使其可以作为 gulp ️一个子任务来执行。只不过当时还是 JQuery 的时代,功能基本重复,真正使用 webpack 的还是很少,所以 reactspa 框架的出现让 webpack 迅速蹿红。

gulp的核心api

task, series, parallel, src, pipe, dest, on, watch

  • task: 创建一个任务

  • series:顺序执行多个任务

  • prallel:并行执行多个任务

  • src:读取数据源转换成stream

  • pipe:管道-可以在中间对数据流进行处理

  • dest:输出数据流到目标路径

  • on:事件监听

  • watch:数据源监听

这些api在demo中都有用一个例子串起来讲解使用

其他的基本很少会用到了,这里就不多复述,网上的很多文章,还有官方的api都有详细的,但在实际的开发中我基本很少用到,可能是使用的场景过于简单吧

本文就用一个实际的例子把这几个api全部串联起来,我将实现一个这样的功能:

流程管理

全局安装gulp

$ npm i gulp -g

项目根目录新建gulpfile.js文件

文件头引入模块

// gulpfile.js
const gulp = require("gulp");
/**
* 合并文件插件
* gulp的插件很多,有4000多个,足够满足大家日常的各种需求,而且插件写起来也超级简单
*/

const concat = require("gulp-concat");
const through2 = require("through2");

创建合并文件任务

新建合并任务,读取 20201108 目录下所有 txt 文件,合并为 20201108.txt 文件并存储在 demo 文件夹下

// task 为创建gulp子任务
gulp.task('concat', () => {
return gulp.src('./20201108/*.txt') // src: 读取文件转化为可读流,参数可以是文件通配符匹配
.pipe(gulpConcat('20201108.txt')) // pipe:管道,把gulp的执行步骤一步步串联起来,也是gulp的核心
.pipe(dest('./demo/')) // dest:存放文件
.on('end', () => { // 事件监听
console.log('concat: 文件合并完成');
})
})

创建文件去除空行任务

因为是需要顺序执行子任务,所以用的 series ,如果是需要并行执行的话用 parallel

代码中的 through2 主要是用来做文件流转换过滤,写 gulp 插件必备,下一节会大概的介绍一下

gulp.task('format', gulp.series('concat', () => {
return gulp.src('./demo/20201108.txt')
.pipe(through2.obj(function (file, encoding, cb) { // through2:文件流转换,写gulp插件必备,下面会大概的介绍一下
let contents = file.contents.toString();
contents = contents
.replace(/(\n[\s\t]*\r*\n)/g, "\n")
.replace(/^[\n\r\n\t]*|[\n\r\n\t]*$/g, ""); // 去除空行
let lines = contents.split(/\n/g);
totalLine = lines.length;
contents = lines.join("\n");
file.contents = Buffer.from(contents);
this.push(file);
cb();
}))
.pipe(dest('./demo/'))
.on('end', () => {
console.log('format: 去除空行完成');
})
}))

创建监听任务

20201108 文件夹下的文件有写入操作时,去执行 format 任务, format 任务又依赖 concat 任务执行

gulp.task('watch', () => {
// 因为是需要顺序执行子任务,所以用的concat,如果是需要并行执行的话用parallel
gulp.watch('./20201108/*.txt', gulp.series('format', (cb) => {
cb();
})).on('change', () => { // 更多事件监听可以查看官方文档
console.log('watch: 文件被改变');
})
})

在项目目录下执行

以上几步的代码合并到一个 gulpfile.js 文件中即可运行

# 监控20201108文件夹下所有文件变化,则执行format子任务
$ gulp watch

下图为命令行中输入日志

图片

看了上面的 demo 可能大家会对 through2 比较好奇吧,接下来会大概介绍一下

gulp插件机制

我们先提一提 gulp 的机制, gulp 内部的实现很简单,用了三个sdk实现 undertakervinyl-fs , glob-watcher

  • undertaker: 主要用来实现gulp的子任务流程管理

  • vinyl-fs: .src 接口可以匹配一个文件 通配符 ,将匹配到的文件转为 Vinyl Stream (流), gulp 理念就是万物皆可流

  • glob-watcher: 也就是去实现 gulp.watch 功能,监控文件流变化

核心就是把文件转换成 Stream 流,然后对 Stream 进行操作。

所以 gulp 采用 pipe (管道)的概念,意味着顺着管道流淌,然后我们对于 gulp 的插件,也很好理解了,就是在管道中间有个过滤站,对流进行过滤处理,这就用到了上面提到的 through2 ,这个插件主要的作用也是对流文件进行处理,类似的插件还有 map-stream 等,不过 gulp 的主流的插件都是基于 through2 编写的.

例如上面的例子(文件去除空行任务),单独封装一下,使用的时候就是一个简单的插件

// gulp-file-format.js

module.exports = () => {
return through2.obj(function (file, encoding, cb) {
let contents = file.contents.toString();
contents = contents
.replace(/(\n[\s\t]*\r*\n)/g, "\n")
.replace(/^[\n\r\n\t]*|[\n\r\n\t]*$/g, ""); // 去除空行
let lines = contents.split(/\n/g);
totalLine = lines.length;
contents = lines.join("\n");
file.contents = Buffer.from(contents);
this.push(file);
cb();
})
}

替换文件去除空行任务

const gulpFormact = require('gulp-file-format.js');

gulp.task('format', gulp.series('concat', () => {
return gulp.src('./demo/20201108.txt')
.pipe(gulpFormact())
.pipe(dest('./demo/'))
.on('end', () => {
console.log('format: 去除空行完成');
})
}))

这就是一个很简单的 gulp 插件了,是不是很简单,比 webpack 的插件简单多了

下面讲一个日常中对于重复工作提效写的一个脚本,讲讲思路,让大家对gulp的使用场景有个更深的理解。

实际应用案例思路拆解-支付中间页改版后数据统计

由于实际的代码涉及到一些敏感数据,所以这个段落只是讲一下解决这个实际问题的思路拆解,怎么去用 gulp 完成想要的结果,不贴详细的代码了。

例如,作者最近做了一个支付中间页的改版

我需要统计从这个支付中间页转化的用户产生了多少收入,人工流程如下:

统计流程

把以上几个步骤拆解成 gulp 的任务,用 gulp 的任务机制管理起来,每一个任务可以单独执行,又可以统一执行

  1. export:下载用户uid

// 导出uid表
gulp.task('export', () => {})
  1. concat:合并文件并去重

// 对excel文件进行合并去重
gulp.task('cocat', () => {})
  1. money:循环uid,远程请求接口,拿到支付金额

// 获取每一个uid的支付金额
gulp.task('money', () => {})
  1. total: 汇总数据,生成汇总excel表格并输出

// 数据汇总
gulp.task('total', gulp.series('export', 'concat', 'money', (cb) => {
//...
}))
  1. 执行命令

$ gulp total

以上任务都可以独立执行,也可以合并执行

更复杂的应用场景-转转sdk生成命令工具

更复杂的应用场景可以查看我们之前产出的一套 sdk 命令生成工具:commander-tools,现已在 github 开源,在转转支撑团队的维护下功能越来强大,主要实现以下命令:

{
"scripts": {
"lint": "commander-tools run lint", // 校验
"fix": "commander-tools run lint --fix", // 修复
"staged": "commander-tools run lint --staged",
"staged-fix": "commander-tools run lint --staged --fix",
"dev": "commander-tools run dev", // 启动本地调试服务
"compile": "commander-tools run compile", // 编译
"dist": "commander-tools run dist", // 外链打包
"analyz": "commander-tools run dist --analyz", // 代码分析
"build": "commander-tools run build",
"pub": "commander-tools run pub", // 发布正式版
"pub-beta": "commander-tools run pub-beta", // 发布beta版本
"unpub": "commander-tools run unpub", // 卸载版本
"doc": "commander-tools run doc", // 预览文档
"build-doc": "commander-tools run build-doc", // 生成文档
"doc-upload": "commander-tools run doc-upload" // 文档上传ftp
}
}

例如:一个上传注释文档的功能

$ npm run doc-upload
/**
* 上传文档
**/

const chalk = require('chalk')
const ftp = require('vinyl-ftp')
gulp.task('doc-upload', gulp.series('build-doc', done => {
console.log(chalk.green('running doc-upload'))

if (!ftpConfig) {
console.log(chalk.red('请配置 ftp.config.js'))
process.exit(1)
} else {
const businessLine = getBusinessLine(program)
const { name } = packageJson
const conn = ftp.create({
parallel: 10,
log: fancyLog,
...ftpConfig
})

const pipe = gulp
.src(`${cwd}/${program.docsDirName || 'docs'}/**/*`)
.pipe(conn.dest(`/${businessLine}/${name}/`))
.on('end', () => {
console.log(chalk.green('Success: 文档上传成功'))
ftp.docUrl && open(ftp.docUrl)
})

return pipe
}
}))

以上命令保证转转的所有 sdk 都能实现按需加载,并且规范化输出

结语

如果只是想用一个很简单的小功能,不用写繁琐的 node 脚本,不用去配置复杂的 webpackgulp 不超过 10 行代码就能帮你搞定,它丰富的插件生态基本能满足你所有的功能需求,简直就是提升开发效率的利器。

参考资料

  • gulp官网

  • gulp插件集合

  • commander-tools

  • 文件通配符

文末福利

转发本文并留下评论,我们将抽取第 10 名留言者(依据公众号后台排序),转转纪念 T 恤一件,大家快转发起来吧~

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章