在前端工程化中最重要的就是流程管理,借用 gulp 可以很方便的基于流的方式定义流程任务,并将任务串联起来,本节中将详细介绍 gulp ,包括:

  • gulp 介绍

    • gulp 是什么

    • gulp 能够解决哪些问题

    • gulp 核心思想和特点

  • gulp 安装

  • gulp 配置和 API 使用

  • gulp 增量 build

2.3.1 Gulp 介绍

The streaming build system , Automate and enhance your workflow

Gulp 是一个基于 Node.js 的开源前端工作流构建工具,目前最新的版本为 3.9.1 ,最新的维护分支已经到了 4.0,更具体一下 Gulp 是:

  • 自动化工具:Gulp 帮助解决开发过程中的流程任务自动化问题

  • 平台无关工具:Gulp 被集成进了大多数的 IDE 中,可以在 PHP, .NET, Node.js, Java 和其他的一些平台上使用 Gulp

  • 构建生态系统:Gulp 拥有完整的插件生态,到目前为止,在 npm.js 上可以搜索到 13589 results for ‘gulp-’ ,基于这些插件几乎可以完整所有的前端构建任务。

我们将使用最新的版本 4.0 来配置 React 的前端工程中。

Gulp 核心思想和特点

  • 流: Gulp 的设计核心是基于流的方式,将文件转化为抽象的流,然后通过管道的方式将任何串联起来,基于流的方式让任务处理都保存在内存当中,没有临时文件,能够提升构建的性能。

  • 基于代码的任务配置: 在 Gulp 之前,我们熟悉的任务构建工具是 Grunt,在 Grunt 的中,所有的任务都是基于配置的方式,然后 Gulp 的方式并非配置,而是通过提供极简的 API ,以代码的方式定义任务,这样在灵活性上极大的提升。

  • 简洁的 API: Gulp 在 API 的设计上极其简洁,极大的降低学习成本,同时在使用上会非常方便。

  • 简单语义化的任务模块: Gulp 的任务以插件的方式完成,插件的任务功能单一,并且语义化,让工作流的定义更加直观,易读。

  • 效率: 在 Gulp 中任务会尽可能的并发执行

Gulp 能够解决哪些问题

通常的一个前端构建流程包括:

  1. 文件清理 (gulp-clean)

  2. 文件拷贝 (gulp-copy)

  3. 文件转换 (gulp-webpack)

  4. 文件合并 (gulp-concat)

  5. 文件压缩 (gulp-minify)

  6. 文件服务 (gulp-connect)

  7. 文件监控 (gulp-watch)

  8. css 相关

    • less,sass 转换 (gulp-less ,gulp-sass)

    • css 自动添加前缀 (gulp-autoprefixer)

  9. js 相关

    • jslint (gulo-eslint)

  10. html 转换

    • html 模板 (gulp-jade,gulp-ejs)

    • html prettify

    • html validator

    • html minifier

这些构建任务在 Gulp 中都可以利用插件很容易的配置出来

一个 Gulp 配置示例

Gulp 通过定义 gulpfile.js 配置文件的方式定义流程,gulp.js 会通过调用 Node.js 来执行

一个简单的流程定义文件为:

var gulp = require('gulp');
var less = require('gulp-less');
var babel = require('gulp-babel');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');
var cleanCSS = require('gulp-clean-css');
var del = require('del'); var paths = {
styles: {
src: 'src/styles/**/*.less',
dest: 'assets/styles/'
},
scripts: {
src: 'src/scripts/**/*.js',
dest: 'assets/scripts/'
}
}; /**
* 并非所有的任务都是基于流,例如删除文件
* 一个 gulpfile 只是一个 Node 程序,在 gulpfile 中可以使用任何 npm 中的模块或者其他 Node.js 程序
*/function clean() {
// del 也可以和 `gulp.src` 一样可以基于模式匹配的文件路径定义方式 return del([ 'assets' ]);
} /*
* 通过 Javascript 函数的方式定义任务
*/function styles() {
return gulp.src(paths.styles.src)
.pipe(less())
.pipe(cleanCSS())
// 传递一些配置选项到 stream 中
.pipe(rename({
basename: 'main',
suffix: '.min'
}))
.pipe(gulp.dest(paths.styles.dest));
} /**
* 编译 coffee 文件,然后压缩代码,然后合并到 all.min.js
* 并生成 coffee 源码的 sourcemap
*/function scripts() {
return gulp.src(paths.scripts.src, { sourcemaps: true })
.pipe(babel())
.pipe(uglify())
.pipe(concat('main.min.js'))
.pipe(gulp.dest(paths.scripts.dest));
} /**
* 监控文件,当文件改变过后做对应的任务
* @return {[type]} [description]
*/function watch() {
gulp.watch(paths.scripts.src, scripts);
gulp.watch(paths.styles.src, styles);
} /*
* 使用 CommonJS `exports` 模块的方式定义任务
*/
exports.clean = clean;
exports.styles = styles;
exports.scripts = scripts;
exports.watch = watch; /*
* 确定任务是以并行还是串行的方式定义任务
*/var build = gulp.series(clean, gulp.parallel(styles, scripts)); /*
* 除了 export 的方式,也可以使用 gulp.task 的方式定义任务
*/
gulp.task('build', build); /*
* 定义默认任务,默认任务可以直接通过 gulp 的方式调用
*/
gulp.task('default', build);

2.3.2 Gulp 安装

$ cd your-project

// 安装最新版本的 gulp-cli
$ npm install gulpjs/gulp-cli -g // 安装最新版本的 gulp 4.0
$ npm install gulpjs/gulp.git#4.0 --save-dev // 检查 gulp 版本
$ gulp -v ---
[10:48:35] CLI version 1.2.1
[10:48:35] Local version 4.0.0-alpha.2

2.3.3 Gulp 配置与 API

任务定义

定义任务有两种方法

第一种方法为 Node.js 模块 exports 的方式:

function someTask() {
...
}
exports.someTask = SomeTask

第二种方法为调用 gulp.task API 的方式

function someTask() {
...
} // api 定义方式 1
gulp.task('someTask', someTask) // ap1 定义方式 2
gulp.task(function someTask() {
...
}); // 获取var someTask = gulp.task('someTask')

任务内容

通常一个任务会以如下方式定义

function someTask() {
return gulp.src(...) // 流的输入
.pipe(someplugin()) // 插件处理流
.pipe(someplugin2()) // 插件处理流
.dest(...) // 输出流
}

任务的异步

task 的执行时异步的,可以基于回调函数 或 promise 或 stream 等方式

回调函数

var del = require('del');
// 传入 done 回调函数
gulp.task('clean', function(done) {
del(['.build/'], done);
});

返回流

gulp.task('somename', function() {
return gulp.src('client/**/*.js')
.pipe(minify())
.pipe(gulp.dest('build'));
});

返回 Promise

var Promise = require('promise');
var del = require('del'); gulp.task('clean', function() {
return new Promise(function (resolve, reject) {
del(['.build/'], function(err) {
if (err) {
reject(err);
} else {
resolve();
}
});
});
});

返回子进程

gulp.task('clean', function() {
return spawn('rm', ['-rf', path.join(__dirname, 'build')]);
});

返回 RxJS observable

var Observable = require('rx').Observable;

gulp.task('sometask', function() {
return Observable.return(42);
});

流的入口 gulp.src

/**
* @param globs [String | Array]
* @param options [Object {
* // 默认: process.cwd()
* // 描述: 工作目录
* cwd: String,
*
* // 默认:在模式匹配之前的路径 a/b/ ** / *.js 路径为 a/b/
* // 描述:gulp.dest 目录会添加 base 目录
* base: String | Number,
* ...
* }]
*/
gulp.src(globs[, options])

gulp.src 方法是流的入口,方法的方法返回的结果为一个 Vinyl files 的 node stream ,可以被 piped 到别的插件中。

gulp.src('client/templates/*.jade')
.pipe(jade())
.pipe(minify())
.pipe(gulp.dest('build/minified_templates'));

匹配模式

gulp.src 的参数 globs 中的 glob 是一种匹配模式,可以使用 **,* 这些通配符来匹配文件,globs 参数可以为一个 glob 匹配字符串,也可以是 glob 匹配字符串数组

假定我们的项目目录结构如下:

.
└── src
├── d1
│   ├── d1-1
│   │   └── f1-1-1.js
│   └── f1-1.js
├── f1.js
├── f2.js
└── f3.js

下面是一些匹配的示例:

src/*.js

匹配结果:

src/f1.js src/f2.js src/f3.js

匹配策略:

匹配 src 一级目录下面的所有 js 文件,同

$ ls src/*.js

* 表示匹配文件名称中的 0 个或者多个字符,* 不匹配 . 开头的文件

src/**/*.js

匹配结果:

src/d1/d1-1/f1-1-1.js src/d1/f1-1.js src/f1.js src/f2.js src/f3.js

匹配策略:

匹配 src 下面的所有 js 文件,同

$ ls src/**/*.js

** 表示匹配所有子目录和当前目录,不包括 symlinked 的目录 (如果要包含需要 options 中传入 follow: true)

src/{d1/*.js,*.js}

匹配结果:

src/d1/f1-1.js src/f1.js src/f2.js src/f3.js

匹配策略:

匹配 src/d1 一级目录下面的 js 和 src 一级目录下面的 js,同:

$ ls src/{d1/*.js,*.js}

{} 内添加 , 可以分割多个匹配

其他匹配模式

  • [...]: 同正则表达式中的中括号,匹配其中的任意字符,如果字符的开头包好为 !^ 表示不匹配其中的任何字符

  • !(pattern|pattern|pattern): 匹配任意不满足其中的文件

  • ?(pattern|pattern|pattern): 匹配 0 个或者 1 个

  • +(pattern|pattern|pattern): 匹配 1 个或者多个

  • *(pattern|pattern|pattern): 匹配 0 个或者多个

  • @(pattern|pattern|pattern): 匹配 1 个

gulp 的匹配使用了 node-glob 更多匹配模式可参考 https://github.com/isaacs/node-glob , gulp.src 还可以通过传递 options 配置 glob 的匹配参数,

流的出口 gulp.dest

/**
* @param path [String]
* @param options [Object {
* // 默认: process.cwd()
* // 描述: 如果提过的 output 目录是相对路径,会将 cwd 作为 output 目录
* cwd: String,
*
* // 默认:file.stat.mode
* // 描述:文件的八进制权限码如 "0744", 如果没有回默认进程权限
* mode: String | Number,
*
* // 默认:process.mode
* // 描述:目录的八进制权限码
* dirMode: String | Number,
*
* // 默认:true
* // 描述:相同路径如果存在文件是否要被覆盖
* overwrite: Boolean
* ....
* }]
*/
gulp.dest(globs[, options])

gulp.dest 可以理解为流的出口,会基于传入的 path 参数和流的 base 路径导出文件。

// 匹配 'client/js/somedir/somefile.js'   // base 为 client/js// 导出 为 'build/somedir/somefile.js'
gulp.src('client/js/**/*.js')
.pipe(minify())
.pipe(gulp.dest('build')); // base 为 client// 导出 为 'build/js/somedir/somefile.js'
gulp.src('client/js/**/*.js', { base: 'client' })
.pipe(minify())
.pipe(gulp.dest('build')); // 'build/js/somedir/somefile.js'

任务的并行与串行

在工作流管理中,有些任务需要串行执行,有些任务可能需要并行执行,Gulp 提供了两个 API 来解决此问题:

  1. gulp.parallel : 并行执行

  2. gulp.series: 串行执行

eg:

gulp.task('one', function(done) {
// do stuff
done();
}); gulp.task('two', function(done) {
// do stuff
done();
}); // 并行任务,任务执行完成可以添加回调函数
gulp.task('parallelTask', gulp.parallel('one', 'two', function(done) {
done();
})); // 串行任务
gulp.task('seriesTask', gulp.series('one', 'two', function(done) {
done();
}));

文件监控

gulp 提供的文件监控 API: gulp.watch

/**
* @param globs [String | Array] 需要监控的文件 globs
* @param opts [Object] https://github.com/paulmillr/chokidar 的配置参数,
*/
gulp.watch(globs[, opts][, fn])

使用示例:

var watcher = gulp.watch('js/**/*.js', gulp.parallel('concat', 'uglify'));
watcher.on('change', function(path, stats) {
console.log('File ' + path + ' was changed');
}); watcher.on('unlink', function(path) {
console.log('File ' + path + ' was removed');
});

2.3.4 gulp 增量 build

每次执行构建任务的时候,为了减少构建时间,可以采用增量构建的方式,在 Gulp 中,可以利用一些插件过滤 stream,找出其中修改过的文件。

以 gulp-newer 为例:

function images() {
var dest = 'build/img';
return gulp.src(paths.images)
.pipe(newer(dest)) // 找出新增加的图像
.pipe(imagemin({optimizationLevel: 5}))
.pipe(gulp.dest(dest));
}

在某些情况过滤掉 stream 过后还需要还原原来的 stream ,比如文件 transform 过后还需要文件合并,这种时候可以利用一下这两个插件:

function scripts() {
return gulp.src(scriptsGlob)
.pipe(cache('scripts')) // 和 newer 类似,过滤出改变了的 scripts
.pipe(header('(function () {')) // 文件添加 header
.pipe(footer('})();')) // 文件添加 footer
.pipe(remember('scripts')) // 找出所有的 scripts
.pipe(concat('app.js')) // 将所有文件合并
.pipe(gulp.dest('public/'))
}

2.3 Gulp的更多相关文章

  1. 构建一个基本的前端自动化开发环境 —— 基于 Gulp 的前端集成解决方案(四)

    通过前面几节的准备工作,对于 npm / node / gulp 应该已经有了基本的认识,本节主要介绍如何构建一个基本的前端自动化开发环境. 下面将逐步构建一个可以自动编译 sass 文件.压缩 ja ...

  2. 常用 Gulp 插件汇总 —— 基于 Gulp 的前端集成解决方案(三)

    前两篇文章讨论了 Gulp 的安装部署及基本概念,借助于 Gulp 强大的 插件生态 可以完成很多常见的和不常见的任务.本文主要汇总常用的 Gulp 插件及其基本使用,需要读者对 Gulp 有一个基本 ...

  3. 在windows下安装gulp —— 基于 Gulp 的前端集成解决方案(一)

    相关连接导航 在windows下安装gulp —— 基于 Gulp 的前端集成解决方案(一) 执行 $Gulp 时发生了什么 —— 基于 Gulp 的前端集成解决方案(二) 常用 Gulp 插件汇总 ...

  4. 执行 $Gulp 时发生了什么 —— 基于 Gulp 的前端集成解决方案(二)

    前言 文章 在windows下安装gulp —— 基于 Gulp 的前端集成解决方案(一) 中,已经完成对 gulp 的安装,由于是window环境,文中特意提到了可以通过安装 gitbash 来代替 ...

  5. gulp详细入门教程

    本文链接:http://www.ydcss.com/archives/18 gulp详细入门教程 简介: gulp是前端开发过程中对代码进行构建的工具,是自动化项目的构建利器:她不仅能对网站资源进行优 ...

  6. 做一个gulp+webpack+vue的单页应用开发架子

    1.目标 最近项目上的事情不多,根据我自己的开发习惯,决定开发一些简单的开发架子,方便以后事情多的时候直接套用.本文讲的一个gulp+webpack+vue的单页应用架子,想要达到的目的: 可以通过命 ...

  7. 前端自动化构建工具gulp记录

    一.安装 1)安装nodejs 通过nodejs的npm安装gulp,插件也可以通过npm安装.windows系统是个.msi工具,只要一直下一步即可,软件会自动在写入环境变量中,这样就能在cmd命令 ...

  8. gulp初学

    原文地址:gulp初学 至于gulp与grunt的区别,用过的人都略知一二,总的来说就是2点: 1.gulp的gulpfile.js  配置简单而且更容易阅读和维护.之所以如此,是因为它们的工作方式不 ...

  9. gulp批量打包文件并提取公共文件

    gulp是前端开发过程中对代码进行构建的工具,是自动化项目的构建利器. browseriyf是模块化打包工具. 一般情况下,Browserify 会把所有的模块打包成单个文件.单个文件在大多数情况下是 ...

  10. 使用gulp解决RequireJS项目前端缓存问题(二)

    1.前言 这一节,我们主要解决在上一节<使用gulp解决RequireJSs项目前端缓存问题(一)>末尾提到的几个问题: 对通过require-config.js引入的js文件修改后,没有 ...

随机推荐

  1. cent OS 7 下安装 python 3.6

    step1:安装依赖环境 # yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readli ...

  2. HTML-参考手册: URL 编码

    ylbtech-HTML-参考手册: URL 编码 1.返回顶部 1. HTML URL 编码 参考手册 URL 编码会将字符转换为可通过因特网传输的格式. URL - 统一资源定位器 Web 浏览器 ...

  3. 调整WebBrowser的默认浏览器内核版本

    原文出自:https://my.oschina.net/Tsybius2014/blog/492107 注:这个是写.net控件,其实delphi是一样的.作者已经写的比较全面了,我只是做了一点修改 ...

  4. git的使用(本地版本库)

    1. 创建版本库 1.1 创建一个版本库非常简单,首先,选择一个合适的地方,创建一个空目录: 1.2通过git init命令把这个目录变成Git可以管理的仓库(git进入空目录的路径) $ git i ...

  5. vue中按需引入mint-UI报Error: .plugins[3][1] must be an object, false, or undefined

    { "presets": ["@babel/preset-env", "@babel/preset-react"], "plugi ...

  6. vue组件级路由钩子函数(beforeRouteEnter/beforeRouteUpdate/beforeRouteLeave)

    1.vue组件级路由钩子函数(beforeRouteEnter/beforeRouteUpdate/beforeRouteLeave):http://www.menvscode.com/detail/ ...

  7. .sync 修饰符的理解

    正常 子组件: this.$emit('update:title', newTitle) 父组件: <text-document v-bind:title="doc.title&quo ...

  8. 二、hibernate的常用API

    hibernate的调用过程 public class demo01 { @Test public void test(){ // 1.加载hibernate核心配置文件 Configuration ...

  9. MS14-068利用

    漏洞原理详情后续补上:kerberos 协议实现过程中的某些 bug,致使普通域用户可以任意伪造高权限 PAC,去请求 TGS 从而导致的权限提升,漏洞现在很少遇到了. 一.利用 需要拥有一个域账号的 ...

  10. shell位置参数变量