14

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

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=MzU0OTExNzYwNg%3D%3D&%3Bmid=2247488733&%3Bidx=1&%3Bsn=6fdf3af75b1dae2d781fb78d87fe3b18
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
r2aUJfJ.png!mobile

前言

可能很多人对于 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全部串联起来,我将实现一个这样的功能:

rMju2ar.png!mobile流程管理

全局安装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

下图为命令行中输入日志

R3URn2a.png!mobile图片

看了上面的 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 完成想要的结果,不贴详细的代码了。

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

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

QrAZnea.png!mobile统计流程

把以上几个步骤拆解成 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 恤一件,大家快转发起来吧~

ziINVzJ.png!mobile

VVbErmy.jpg!mobile

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK