前言
可能很多人对于gulp
都相对陌生,特别是vue
,react
出现以后,渐渐淡出了做业务前端人员的视野,14到16年的时候应该是它最巅峰的时候,真正的是出道即巅峰,取代了当时最火的grunt
成为了前端构建的主流工具,就连某度都忍不住来瓜分一下流量,出了个fis
(不过按照烂尾的惯例来看,基本会属于后继无人的状态,所以没有真正去用在生产项目中过),而且当时webpack
虽然已经出现,但完全不能跟gulp
抗衡,直到vue
,react
等spa
项目出现,才让webpack
取而代之,gulp
也逐渐退出幕前,转战幕后,去做了它更擅长的事情:前端开发流程规范管理。
现在我们在各种组件库,像antd
,element-ui
,vant
等比较人们的组件库,或者其他一些前端工程中都能看到它的身影,只不过它不再介入到业务的实际生产开发中了,所以对业务开发人员来说是不太能感知到它的存在了。
gulp和webpack的区别
首先,可能很多人面试过程中都会被问到这个问题。我说一说自己的理解:
gulp | webpack |
---|---|
强调的是规范前端开发的流程 | 是一个前端模块化方案 |
是一个基于流的自动化构建工具,不包括模块化的功能,通过配置一系列的task,例如文件压缩合并、雪碧图、启动server、版本控制等,然后定义执行顺序来让gulp执行task,从而构建前端项目的流程 | 是一个自动化模块打包工具,把开发中的所有资源(图片、js文件、css文件等)都看成模块,通过loader(加载器)和plugins(插件)对资源进行处理,划分成不同的模块,需要哪个加载哪个,实现按需加载的功能,入口引入的更多是js文件 |
在webpack
刚面世的时候,webpack
在gulp
中也有一个插件(gulp-webpack
)作为使其可以作为gulp
️一个子任务来执行。只不过当时还是JQuery
的时代,功能基本重复,真正使用webpack
的还是很少,所以react
等spa
框架的出现让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
代码语言:javascript复制$ npm i gulp -g
项目根目录新建gulpfile.js文件
文件头引入模块
代码语言:javascript复制// 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[st]*r*n)/g, "n")
.replace(/^[nrnt]*|[nrnt]*$/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实现undertaker
,vinyl-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
编写的.
例如上面的例子(文件去除空行任务),单独封装一下,使用的时候就是一个简单的插件
代码语言:javascript复制// gulp-file-format.js
module.exports = () => {
return through2.obj(function (file, encoding, cb) {
let contents = file.contents.toString();
contents = contents
.replace(/(n[st]*r*n)/g, "n")
.replace(/^[nrnt]*|[nrnt]*$/g, ""); // 去除空行
let lines = contents.split(/n/g);
totalLine = lines.length;
contents = lines.join("n");
file.contents = Buffer.from(contents);
this.push(file);
cb();
})
}
替换文件去除空行任务
代码语言:javascript复制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
的任务机制管理起来,每一个任务可以单独执行,又可以统一执行
- export:下载用户uid
// 导出uid表
gulp.task('export', () => {})
- concat:合并文件并去重
// 对excel文件进行合并去重
gulp.task('cocat', () => {})
- money:循环uid,远程请求接口,拿到支付金额
// 获取每一个uid的支付金额
gulp.task('money', () => {})
- total: 汇总数据,生成汇总excel表格并输出
// 数据汇总
gulp.task('total', gulp.series('export', 'concat', 'money', (cb) => {
//...
}))
- 执行命令
$ 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
}
}
例如:一个上传注释文档的功能
代码语言:javascript复制$ npm run doc-upload
代码语言:javascript复制/**
* 上传文档
**/
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
脚本,不用去配置复杂的webpack
,gulp
不超过10
行代码就能帮你搞定,它丰富的插件生态基本能满足你所有的功能需求,简直就是提升开发效率的利器。
参考资料
- gulp官网
- gulp插件集合
- commander-tools
- 文件通配符
公众号:前端食堂
知乎:童欧巴
掘金:童欧巴
这是一个终身学习的男人,他在坚持自己热爱的事情,欢迎你加入前端食堂,和这个男人一起开心的变胖~