博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
深入理解gulp自动化
阅读量:4087 次
发布时间:2019-05-25

本文共 46904 字,大约阅读时间需要 156 分钟。

写在前面

http://jafeney.com/2016/03/06/2016-03-06-gulp/     gulp使用了有一段时间,公司好几个项目自动化就是用它构建的。不过对这个简单粗暴的工具我常常是又爱又怕。啥意思呢?大牛们写的gulp任务我看得懂,也能依样画葫芦运用到自己的项目,但是如果撇开doc,让我自己写我还真写不好。纸上得来终觉浅啊,考验对一门技术的掌握程度,光看得懂、会套用还是停留在浅层,在理解的基础上自己能写出漂亮又高效的代码那才是真正掌握了。

详解gulp的API

要运行gulp任务,只需切换到存放gulpfile.js文件的目录(windows平台请使用cmd或者Power Shell等工具),然后在命令行中执行gulp命令就行了,gulp后面可以加上要执行的任务名,例如gulp task1,如果没有指定任务名,则会执行任务名为default的默认任务。

使用gulp,仅需知道4个API即可:gulp.task(),gulp.src(),gulp.dest(),gulp.watch(),所以很容易就能掌握,但有几个地方需理解透彻才行。往下看…

gulp.src()

获取文件流。注意这个流里的内容不是原始的文件流,而是一个虚拟文件对象流(Vinyl files),这个虚拟文件对象中存储着原始文件的路径、文件名、内容等信息,基本语法为:

1
gulp.src([globs],options)

globs参数是文件匹配模式(类似正则表达式),用来匹配文件路径(包括文件名),当然这里也可以直接指定某个具体的文件路径。当有多个匹配模式时,该参数可以为一个数组。
options为可选参数。通常情况下我们不需要用到。

下面重点介绍gulp常用的glob匹配规则和技巧:

  • * 匹配文件路径中的0个或多个字符,但不会匹配路径分隔符,除非路径分隔符出现在末尾
  • ** 匹配路径中的0个或多个目录及其子目录,需要单独出现,即它左右不能有其他东西了。如果出现在末尾,也能匹配文件。
  • ? 匹配文件路径中的一个字符(不会匹配路径分隔符)
  • [...] 匹配方括号中出现的字符中的任意一个,当方括号中第一个字符为^或!时,则表示不匹配方括号中出现的其他字符中的任意一个,类似js正则表达式中的用法
  • !(pattern|pattern|pattern) 匹配任何与括号中给定的任一模式都不匹配的
  • ?(pattern|pattern|pattern) 匹配括号中给定的任一模式0次或1次,类似于js正则中的 (pattern|pattern|pattern)?
  • +(pattern|pattern|pattern) 匹配括号中给定的任一模式至少1次,类似于js正则中的(pattern|pattern|pattern)+
  • *(pattern|pattern|pattern) 匹配括号中给定的任一模式0次或多次,类似于js正则中的(pattern|pattern|pattern)*
  • @(pattern|pattern|pattern) 匹配括号中给定的任一模式1次,类似于js正则中的(pattern|pattern|pattern)

语法看起来太枯燥,我们还是结合实际场景看看:

  • * 能匹配 a.jsx.yabcabc/,但不能匹配a/b.js
  • *.* 能匹配 a.jsstyle.cssa.b,x.y
  • */*/*.js 能匹配 a/b/c.jsx/y/z.js,不能匹配a/b.jsa/b/c/d.js
  • ** 能匹配 abca/b.jsa/b/c.jsx/y/zx/y/z/a.b,能用来匹配所有的目录和文件
  • **/*.js 能匹配 foo.jsa/foo.jsa/b/foo.jsa/b/c/foo.js
  • a/**/z 能匹配 a/za/b/za/b/c/za/d/g/h/j/k/z
  • a/**b/z 能匹配 a/b/za/sb/z,但不能匹配a/x/sb/z,因为只有单**单独出现才能匹配多级目录
  • ?.js 能匹配 a.jsb.jsc.js
  • a?? 能匹配 a.babc,但不能匹配ab/,因为它不会匹配路径分隔符
  • [xyz].js 只能匹配 x.jsy.jsz.js,不会匹配 xy.jsxyz.js等,整个中括号只代表一个字符
  • [^xyz].js 能匹配 a.jsb.jsc.js等,不能匹配 x.jsy.jsz.js

当有多种匹配模式时可以使用数组:

1
gulp.src(['js/*.js','css/*.css','*.html'])

使用数组的方式还有一个好处就是可以很方便的使用排除模式,在数组中的单个匹配模式前加上!即是排除模式,它会在匹配的结果中排除这个匹配,要注意一点的是不能在数组中的第一个元素中使用排除模式。

1     
2
gulp.src([*.js,'!b*.js']) //匹配所有js文件,但排除掉以b开头的js文件     
gulp.src(['!b*.js',*.js]) //不会排除任何文件,因为排除模式不能出现在数组的第一个元素中

此外,还可以使用展开模式。 展开模式以花括号作为定界符,根据它里面的内容,会展开为多个模式,最后匹配的结果为所有展开的模式相加起来得到的结果。展开的例子如下:

  • a{b,c}d 会展开为 abdacd
  • a{b,}c 会展开为 abcac
  • a{0..3}d 会展开为 a0da1da2da3d
  • a{b,c{d,e}f}g 会展开为 abgacdfgacefg
  • a{b,c}d{e,f}g 会展开为 abdegacdegabdegabdfg

gulp.dest()

写文件。基本语法为:

1
gulp.dest([path],options)

path为写入文件的路径,options为一个可选的参数对象,通常我们不需要用到。

要想使用好gulp.dest()这个方法,就要理解给它传入的路径参数与最终生成的文件的关系。

gulp的使用流程一般是这样子的:首先通过gulp.src()方法获取到我们想要处理的文件流,然后把文件流通过pipe方法导入到gulp的插件中,最后把经过插件处理后的流再通过pipe方法导入到gulp.dest()中,gulp.dest()方法则把流中的内容写入到文件中,这里首先需要弄清楚的一点是,我们给gulp.dest()传入的路径参数,只能用来指定要生成的文件的目录,而不能指定生成文件的文件名,它生成文件的文件名使用的是导入到它的文件流自身的文件名,所以生成的文件名是由导入到它的文件流决定的,即使我们给它传入一个带有文件名的路径参数,然后它也会把这个文件名当做是目录名,例如:

1     
2
3
4
var gulp = require('gulp');     
gulp.src('script/jquery.js')
.pipe(gulp.dest('dist/foo.js'));
//最终生成的文件路径为 dist/foo.js/jquery.js,而不是dist/foo.js

要想改变文件名,可以使用插件 

下面说说生成的文件路径与我们给gulp.dest() 方法传入的路径参数之间的关系。
gulp.dest(path) 生成的文件路径是我们传入的path参数后面再加上gulp.src()中有通配符开始出现的那部分路径。例如:

1     
2
3
4
5
var gulp = reruire('gulp');     
//有通配符开始出现的那部分路径为 **/*.js
gulp.src('script/**/*.js')
.pipe(gulp.dest('dist')); //最后生成的文件路径为 dist/**/*.js
//如果 **/*.js 匹配到的文件为 jquery/jquery.js ,则生成的文件路径为 dist/jquery/jquery.js

再举更多一点的例子

1     
2
3
4
5
6
7
8
9
10
11
gulp.src('script/avalon/avalon.js') //没有通配符出现的情况     
.pipe(gulp.dest('dist')); //最后生成的文件路径为 dist/avalon.js
//有通配符开始出现的那部分路径为 **/underscore.js
gulp.src('script/**/underscore.js')
//假设匹配到的文件为script/util/underscore.js
.pipe(gulp.dest('dist')); //则最后生成的文件路径为 dist/util/underscore.js
gulp.src('script/*') //有通配符出现的那部分路径为 *
//假设匹配到的文件为script/zepto.js
.pipe(gulp.dest('dist')); //则最后生成的文件路径为 dist/zepto.js

通过指定gulp.src()方法配置参数中的base属性,我们可以更灵活的来改变gulp.dest()生成的文件路径。当我们没有在gulp.src()方法中配置base属性时,base的默认值为通配符开始出现之前那部分路径,例如:

1
gulp.src('app/src/**/*.css') //此时base的值为 app/src

上面我们说的gulp.dest()所生成的文件路径的规则,其实也可以理解成,用我们给gulp.dest()传入的路径替换掉gulp.src()中的base路径,最终得到生成文件的路径。

1     
2
3
gulp.src('app/src/**/*.css') /*此时base的值为app/src,也就是说它的base路径为app/src*/     
/*设该模式匹配到了文件 app/src/css/normal.css*/
.pipe(gulp.dest('dist')) /*用dist替换掉base路径,最终得到 dist/css/normal.css*/

所以改变base路径后,gulp.dest()生成的文件路径也会改变

1     
2
3
4
5
6
7
gulp.src(script/lib/*.js) //没有配置base参数,此时默认的base路径为script/lib     
//假设匹配到的文件为script/lib/jquery.js
.pipe(gulp.dest('build')) //生成的文件路径为 build/jquery.js
gulp.src(script/lib/*.js, {base:'script'}) //配置了base参数,此时base路径为script
//假设匹配到的文件为script/lib/jquery.js
.pipe(gulp.dest('build')) //此时生成的文件路径为 build/lib/jquery.js

gulp.dest()把文件流写入文件后,文件流仍然可以继续使用。

gulp.task()

gulp.task方法用来定义任务,内部使用的是Orchestrator,其语法为:

1
gulp.task([name],[deps], fn)

name 为任务名。deps 是当前定义的任务需要依赖的其他任务,为一个数组。当前定义的任务会在所有依赖的任务执行完毕后才开始执行。如果没有依赖,则可省略这个参数。fn 为任务函数,我们把任务要执行的代码都写在里面。该参数也是可选的。

1     
2
3
gulp.task('mytask', ['array', 'of', 'task', 'names'], function() { //定义一个有依赖的任务     
// Do something
});

gulp.task()这个API没什么好讲的,但需要知道执行多个任务时怎么来控制任务执行的顺序。
gulp中执行多个任务,可以通过任务依赖来实现。例如我想要执行one,two,three这三个任务,那我们就可以定义一个空的任务,然后把那三个任务当做这个空的任务的依赖就行了:

1     
2
//只要执行default任务,就相当于把one,two,three这三个任务执行了     
gulp.task('default',['one','two','three']);

如果任务相互之间没有依赖,任务会按你书写的顺序来执行,如果有依赖的话则会先执行依赖的任务。但是如果某个任务所依赖的任务是异步的,就要注意了,gulp并不会等待那个所依赖的异步任务完成,而是会接着执行后续的任务。例如:

1     
2
3
4
5
6
7
8
9
10
11
gulp.task('one',function(){     
//one是一个异步执行的任务
setTimeout(function(){
console.log('one is done')
},5000);
});
//two任务虽然依赖于one任务,但并不会等到one任务中的异步操作完成后再执行
gulp.task('two',['one'],function(){
console.log('two is done');
});

上面的例子中我们执行two任务时,会先执行one任务,但不会去等待one任务中的异步操作完成后再执行two任务,而是紧接着执行two任务。所以two任务会在one任务中的异步操作完成之前就执行了。

那如果我们想等待异步任务中的异步操作完成后再执行后续的任务,该怎么做呢?有三种方法可以实现:

第一:在异步操作完成后执行一个回调函数来通知gulp这个异步任务已经完成,这个回调函数就是任务函数的第一个参数。

1     
2
3
4
5
6
7
8
9
10
11
12
gulp.task('one',function(cb){ //cb为任务函数提供的回调,用来通知任务已经完成     
//one是一个异步执行的任务
setTimeout(function(){
console.log('one is done');
cb(); //执行回调,表示这个异步任务已经完成
},5000);
});
//这时two任务会在one任务中的异步操作完成后再执行
gulp.task('two',['one'],function(){
console.log('two is done');
});

第二:定义任务时返回一个流对象。适用于任务就是操作gulp.src获取到的流的情况。

1     
2
3
4
5
6
7
8
9
10
gulp.task('one',function(cb){     
var stream = gulp.src('client/**/*.js')
.pipe(dosomething()) //dosomething()中有某些异步操作
.pipe(gulp.dest('build'));
return stream;
});
gulp.task('two',['one'],function(){
console.log('two is done');
});

第三:返回一个promise对象,例如

1     
2
3
4
5
6
7
8
9
10
11
12
13
var Q = require('q'); //一个著名的异步处理的库 https://github.com/kriskowal/q     
gulp.task('one',function(cb){
var deferred = Q.defer();
// 做一些异步操作
setTimeout(function() {
deferred.resolve();
}, 5000);
return deferred.promise;
});
gulp.task('two',['one'],function(){
console.log('two is done');
});

gulp.task()就这些了,主要是要知道当依赖是异步任务时的处理。

gulp.watch()

监视文件的变化,当文件发生变化后,我们可以利用它来执行相应的任务,例如文件压缩等。其语法为:

1
gulp.watch([glob], [opts], [tasks])

glob 为要监视的文件匹配模式,规则和用法与gulp.src()方法中的glob相同。opts 为一个可选的配置对象,通常不需要用到。tasks 为文件变化后要执行的任务,为一个数组。

1     
2
3
4
5
6
7
gulp.task('uglify',function(){     
//do something
});
gulp.task('reload',function(){
//do something
});
gulp.watch('js/**/*.js', ['uglify','reload']);

gulp.watch() 还有另外一种使用方式:

1
gulp.watch([glob], [opts], cb)

globopts参数与第一种用法相同。cb参数为一个函数。每当监视的文件发生变化时,就会调用这个函数,并且会给它传入一个对象,该对象包含了文件变化的一些信息,type属性为变化的类型,可以是added,changed,deletedpath属性为发生变化的文件的路径。

1     
2
3
4
gulp.watch('js/**/*.js', function(event){     
console.log(event.type); //变化类型 added为新增,deleted为删除,changed为改变
console.log(event.path); //变化的文件的路径
});

常用的gulp插件

自动加载插件

使用 
安装:npm install --save-dev gulp-load-plugins
要使用gulp的插件,首先得用require来把插件加载进来,如果我们要使用的插件非常多,那我们的gulpfile.js文件开头可能就会是这个样子的:

1     
2
3
4
5
6
7
8
9
10
11
var gulp = require('gulp'),     
//一些gulp插件,abcd这些命名只是用来举个例子
a = require('gulp-a'),
b = require('gulp-b'),
c = require('gulp-c'),
d = require('gulp-d'),
e = require('gulp-e'),
f = require('gulp-f'),
g = require('gulp-g'),
//更多的插件...
z = require('gulp-z');

虽然这没什么问题,但会使我们的gulpfile.js文件变得很冗长,看上去不那么舒服。gulp-load-plugins插件正是用来解决这个问题。
gulp-load-plugins这个插件能自动帮你加载package.json文件里的gulp插件。例如假设你的package.json文件里的依赖是这样的:

1     
2
3
4
5
6
7
8
{     
"devDependencies": {
"gulp": "~3.6.0",
"gulp-rename": "~1.2.0",
"gulp-ruby-sass": "~0.4.3",
"gulp-load-plugins": "~0.5.1"
}
}

然后我们可以在gulpfile.js中使用gulp-load-plugins来帮我们加载插件:

1     
2
3
var gulp = require('gulp');     
//加载gulp-load-plugins插件,并马上运行它
var plugins = require('gulp-load-plugins')();

然后我们要使用gulp-renamegulp-ruby-sass这两个插件的时候,就可以使用plugins.renameplugins.rubySass来代替了,也就是原始插件名去掉gulp-前缀,之后再转换为驼峰命名。
实质上gulp-load-plugins是为我们做了如下的转换:

1     
2
plugins.rename = require('gulp-rename');     
plugins.rubySass = require('gulp-ruby-sass');

gulp-load-plugins并不会一开始就加载所有package.json里的gulp插件,而是在我们需要用到某个插件的时候,才去加载那个插件。最后要提醒的一点是,因为gulp-load-plugins是通过你的package.json文件来加载插件的,所以必须要保证你需要自动加载的插件已经写入到了package.json文件里,并且这些插件都是已经安装好了的。

重命名

使用 
安装:npm install --save-dev gulp-rename
用来重命名文件流中的文件。用gulp.dest()方法写入文件时,文件名使用的是文件流中的文件名,如果要想改变文件名,那可以在之前用gulp-rename插件来改变文件流中的文件名。

1     
2
3
4
5
6
7
8
9
10
11
var gulp = require('gulp'),     
rename = require('gulp-rename'),
uglify = require("gulp-uglify");
gulp.task('rename', function () {
gulp.src('js/jquery.js')
.pipe(uglify()) //压缩
.pipe(rename('jquery.min.js')) //会将jquery.js重命名为jquery.min.js
.pipe(gulp.dest('js'));
//关于gulp-rename的更多强大的用法请参考https://www.npmjs.com/package/gulp-rename
});

js文件压缩

使用 
安装:npm install --save-dev gulp-uglify
用来压缩js文件,使用的是uglify引擎

1     
2
3
4
5
6
7
8
var gulp = require('gulp'),     
uglify = require("gulp-uglify");
gulp.task('minify-js', function () {
gulp.src('js/*.js') // 要压缩的js文件
.pipe(uglify()) //使用uglify进行压缩,更多配置请参考:
.pipe(gulp.dest('dist/js')); //压缩后的路径
});

js模块化构建

使用 
安装:npm install --save-dev gulp-browserify

前端js代码的模块化一直是前端工程师之痛,因为JS在ES6之前不支持 import 或者 require 这类的模块加载能力,需要借助 一些工具,比如 requireJSseaJS等。但是 有了 browserify,js代码代码只要编译之后,你可以大胆地 使用require(),就像在node环境里一样,而且它既支持commonJS规范,也支持非commonJS规范,但是为了提高准确率,还是自觉遵守commonJS规范吧!

1     
2
3
4
5
6
7
8
9
var gulp = require('gulp');     
var browserify = require('gulp-browserify');
// 处理javascript
gulp.task('scripts', ['clean'], function() {
return gulp.src('src/js/**/*.js')
.pipe(browserify())
.pipe(gulp.dest(JS_DEST));
});

css文件压缩

使用 
安装:npm install --save-dev gulp-minify-css
要压缩css文件时可以使用该插件

1     
2
3
4
5
6
7
8
var gulp = require('gulp'),     
minifyCss = require("gulp-minify-css");
gulp.task('minify-css', function () {
gulp.src('css/*.css') // 要压缩的css文件
.pipe(minifyCss()) //压缩css
.pipe(gulp.dest('dist/css'));
});

html文件压缩

使用 
安装:npm install --save-dev gulp-minify-html
用来压缩html文件

1     
2
3
4
5
6
7
8
var gulp = require('gulp'),     
minifyHtml = require("gulp-minify-html");
gulp.task('minify-html', function () {
gulp.src('html/*.html') // 要压缩的html文件
.pipe(minifyHtml()) //压缩
.pipe(gulp.dest('dist/html'));
});

js代码检查

使用 
安装:npm install --save-dev gulp-jshint
用来检查js代码

1     
2
3
4
5
6
7
8
var gulp = require('gulp'),     
jshint = require("gulp-jshint");
gulp.task('jsLint', function () {
gulp.src('js/*.js')
.pipe(jshint())
.pipe(jshint.reporter()); // 输出检查结果
});

文件合并

使用 
安装:npm install --save-dev gulp-concat
用来把多个文件合并为一个文件,我们可以用它来合并js或css文件等,这样就能减少页面的http请求数了

1     
2
3
4
5
6
7
8
var gulp = require('gulp'),     
concat = require("gulp-concat");
gulp.task('concat', function () {
gulp.src('js/*.js') //要合并的文件
.pipe(concat('all.js')) // 合并匹配到的js文件并命名为 "all.js"
.pipe(gulp.dest('dist/js'));
});

less和sass的编译

less使用 
安装:npm install --save-dev gulp-less

1     
2
3
4
5
6
7
8
var gulp = require('gulp'),     
less = require("gulp-less");
gulp.task('compile-less', function () {
gulp.src('less/*.less')
.pipe(less())
.pipe(gulp.dest('dist/css'));
});

sass使用 
安装:npm install --save-dev gulp-sass

1     
2
3
4
5
6
7
8
var gulp = require('gulp'),     
sass = require("gulp-sass");
gulp.task('compile-sass', function () {
gulp.src('sass/*.sass')
.pipe(sass())
.pipe(gulp.dest('dist/css'));
});

图片压缩

可以使用 插件来压缩jpg、png、gif等图片。
安装:npm install --save-dev gulp-imagemin

1     
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var gulp = require('gulp');     
var imagemin = require('gulp-imagemin');
var pngquant = require('imagemin-pngquant'); //png图片压缩插件
var cache = require('gulp-cache'); //只压缩修改的图片,没有修改的图片直接从缓存文件读取
gulp.task('default', function () {
return gulp.src('src/images/*')
.pipe(cache(imagemin({
optimizationLevel: 5, //类型:Number 默认:3 取值范围:0-7(优化等级)
progressive: true, //类型:Boolean 默认:false 无损压缩jpg图片
interlaced: true, //类型:Boolean 默认:false 隔行扫描gif进行渲染
multipass: true, //类型:Boolean 默认:false 多次优化svg直到完全优化
svgoPlugins: [{removeViewBox: false}], //不要移除svg的viewbox属性
use: [pngquant()] //使用pngquant来压缩png图片
})))
.pipe(gulp.dest('dist'));
});

gulp-imagemin的使用比较复杂一点,而且它本身也有很多插件,建议去它的项目看看文档

自动刷新

使用插件,安装: npm install --save-dev gulp-livereload
当代码变化时,它可以帮我们自动刷新页面
该插件最好配合谷歌浏览器来使用,且要安装 livereload chrome extension 扩展插件,不能下载的请自行FQ。

1     
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var gulp = require('gulp'),     
less = require('gulp-less'),
livereload = require('gulp-livereload');
gulp.task('less', function() {
gulp.src('less/*.less')
.pipe(less())
.pipe(gulp.dest('css'))
.pipe(livereload());
});
gulp.task('watch', function() {
livereload.listen(); //要在这里调用listen()方法
gulp.watch('less/*.less', ['less']);
});

清理编译环境

使用 
安装 npm install gulp-clean --save-dev
提交代码之前我们需要把本地的编译环境清理干净。

1     
2
3
4
var clean = require('gulp-clean');     
gulp.task('clean',function(){
return gulp.src(['dist/assets/css','dist/assets/js','dist/assets/img'],{read: false}).pipe(clean());
});

我们可以传入一个目录(或档案)阵列到gulp.src()。因为我们不需要读取已经被删除的档案,我们可以加入read:false 选项来防止gulp读取档案内容让它快一些。

我的gulpfile.js

下面的是我花了一下午研究出来的gulp前端自动化管理的最佳实践的gulpfile.js这个文件的代码,想看整体代码的请戳这里 

1     
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
'use strict';     
var gulp = require('gulp'), // gulp核心模块
DEST = 'build', // 编译目录
CSS_DEST = 'build/css', // css编译目录
JS_DEST = 'build/js', // js编译目录
IMG_DEST = 'build/img', // img编译目录
HTML_DEST = 'build/html', // html编译目录
WEB_PORT = 9000, // 服务器监听的端口
$ = require('gulp-load-plugins')(); // gulp插件加载模块
/**
* --------------------------------------
* 注意:下面注释的这些代码可以删除,插件用$加载
* 但是用这种方式加载的插件必须在 package.json里
*/
// less = require('gulp-less'), // less与编译模块
// autoprefixer = require('gulp-autoprefixer'), // 浏览器前缀自动补全
// minifyCss = require('gulp-minify-css'), // 压缩css
// minifyHtml = require("gulp-minify-html"), // 压缩html
// jshint = require('gulp-jshint'), // js语法校验
// browserify = require('gulp-browserify'), // js模块化构建工具
// uglify = require('gulp-uglify'), // 压缩js
// imagemin = require('gulp-imagemin'), // 压缩图片
// rename = require('gulp-rename'), // 文件重命名
// clean = require('gulp-clean'), // 文件清理
// notify = require('gulp-notify'), // 消息通知
// cache = require('gulp-cache'), // 缓存
// sequence = require('gulp-sequence'), // gulp任务执行队列
// connect = require('gulp-connect'), // node本地服务器
// livereload = require('gulp-livereload'); // 浏览器即时刷新
//---------------------------------------
// 处理less
gulp.task('styles', function() {
return gulp.src('src/less/**/*.less')
.pipe($.less())
.pipe($.autoprefixer('last 2 version','safari 5','ie 8','ie 9','opera 12.1','ios 6','android 4'))
.pipe(gulp.dest(CSS_DEST))
.pipe($.rename({
suffix: '.min'
}))
.pipe($.minifyCss())
.pipe(gulp.dest(CSS_DEST))
.pipe($.livereload())
.pipe($.notify({
message: 'Styles task complete'
}));
});
// 处理javascript
gulp.task('scripts', function() {
return gulp.src('src/js/**/*.js')
.pipe($.jshint('.jshintrc'))
.pipe($.jshint.reporter('default'))
.pipe($.browserify())
.pipe(gulp.dest(JS_DEST))
.pipe($.rename({
suffix: '.min'
}))
.pipe($.uglify())
.pipe(gulp.dest(JS_DEST))
.pipe($.livereload())
.pipe($.notify({
message: 'Scripts task complete'
}));
});
// 处理图片
gulp.task('images', function() {
return gulp.src('src/img/**/*')
.pipe($.cache($.imagemin({
optimizationLevel: 3,
progressive: true,
interlaced: true
})))
.pipe(gulp.dest(IMG_DEST))
.pipe($.livereload())
.pipe($.notify({
message: 'Images task complete'
}))
});
// 处理html
gulp.task('htmls', function() {
return gulp.src('src/html/**/*.html')
.pipe($.rename({
suffix: '.min'
}))
.pipe($.minifyHtml())
.pipe(gulp.dest(HTML_DEST))
.pipe($.livereload())
.pipe($.notify({
message: 'Htmls task complete'
}))
});
// 清理build目录
gulp.task('clean', function() {
return gulp.src([HTML_DEST,JS_DEST,CSS_DEST,IMG_DEST], {
read: false
})
.pipe($.clean())
.pipe($.notify({
message: 'Clean task complete'
}));
});
// 设置服务器
gulp.task('http', function() {
$.connect.server({
root: DEST,
port: WEB_PORT,
livereload: true
});
});
// 监听文件变化
gulp.task('watch', function() {
// 监听livereload
$.livereload.listen();
// 监听less
gulp.watch('src/less/**/*.less', ['styles']);
// 监听js
gulp.watch('src/js/**/*.js', ['scripts']);
// 监听图片
gulp.watch('src/img/**/*', ['images']);
// 监听html
gulp.watch('src/html/**/*.html', ['htmls']);
});
// build任务
gulp.task('build', function(cb){
$.sequence('clean',['styles','scripts','images','htmls','watch'])(cb)
});
// 主任务
gulp.task('main', function(cb){
$.sequence('build', ['http'])(cb)
});
// 默认任务
gulp.task('default',['main']);

重构gulpfile.js

重构迭代1: 拆分任务

最先是按照 … 此文档中的架构进行迭代的。

文件结构

1     
2
3
4
5
gulpfile.js     
tasks/
├── xxxx.js
├── xxxx.js
└── image.js

image.js

1     
2
3
4
5
6
7
8
9
10
11
var imagemin = require('gulp-imagemin');     
gulp.task('img', function() {
return gulp.src('./images/**/*.*')
.pipe(imagemin({
optimizationLevel: 2,
progressive: true
}))
.pipe(gulp.dest('./imagemini'))
});

gulpfile.js

1     
2
var requireDir = require('require-dir')     
, tasks = requireDir('./tasks');

这种文件架构让任务按照类型分成子任务放在单独的文件中,顿时感觉干净了很多,这时可以自由的添加子任务,而不用管 gulpfile.js, 此时子任务好比插件,需要就添加,没用就删除,相当方便。

重构迭代2: 避免模块和插件重复依赖

随着时间的推移,发现这种组织架构还是有些不方便,不方便在哪里呢,每一个任务文件中,我都要写 var xxx = require(‘xxx’), 如果你是用上面的架构,任务多的时候,估计也会抓狂,因为你会发现 插件和模块依赖被重复的引入进来,这样就提高了成本。

我不想在子任务文件中重复的引入 插件或模块依赖,有没有上面好方法,stackoverflow 是个好老师,老师告知:

  • 使用 gulp-load-plugins 插件
  • 地址:
  • 把子任务封装成模块

代码结构

1     
2
3
4
5
gulpfile.js     
tasks/
├── xxxx.js
├── xxxx.js
└── image.js

gulpfile.js

1     
2
3
4
5
6
var gulp = require('gulp'),     
gulpLoadPlugins = require('gulp-load-plugins');
// 这里请查看文档
gulpLoadPlugins.imagemin = require('gulp-imagemin');
require('./tasks/image')(gulp, gulpLoadPlugins);

image.js

1     
2
3
4
5
6
7
8
9
10
11
module.exports = function (gulp, Plugin) {     
gulp.task('img', function() {
return gulp.src('./images/**/*.*')
.pipe(Plugin.imagemin({
optimizationLevel: 2,
progressive: true
}))
.pipe(gulp.dest('./imagemini'))
});
};

运行任务 一切正常,此时一个文件测试已经ok。

但是 ./tasks 下面是有很多的子任务,所以需要一个迭代加载,修改 gulpfile.js 如下

1     
2
3
4
5
6
7
8
9
10
11
12
var gulp = require('gulp')     
, gulpLoadPlugins = require('gulp-load-plugins')
// 这里获取子任务文件列表 使用了 fs 模块
, gulpTaskList = require('fs').readdirSync('./tasks/')
;
// 这里请查看文档
gulpLoadPlugins.imagemin = require('gulp-imagemin');
gulpTaskList.forEach(function(taskfile) {
require('./tasks/' + taskfile)(gulp, gulpLoadPlugins);
});

这一次迭代避免了重复依赖的问题,但是你会发现,所有的依赖都声明在 gulpTaskList 命名空间下,如果你依赖很多插件或模块,gulpfile.js 也是相当长,鱼和熊掌不可兼得,在现在情况下,只能寻找最佳的解决方案。

重构迭代3: 参数配置全局化

其实第二部迭代之后,就可以满足大部分需求,但还是有小伙伴抱怨,有些子任务有相同的参数,能不能抽取出来,放到一个单独的文件中,so 继续翻阅文档。

参考文档 …

代码结构

1     
2
3
4
5
6
7
gulpfile.js     
gulp
├── config.json
├── tasks/
├── xxxx.js
├── xxxx.js
└── image.js

注意:文件夹层次变了

config.json

1     
2
3
{     
"pnglevel": 2
}

gulpfile.js

1     
2
3
4
5
6
7
8
9
10
11
var gulp = require('gulp')     
, config = require('./gulp/config.json');
, gulpLoadPlugins = require('gulp-load-plugins')
, gulpTaskList = require('fs').readdirSync('./gulp/tasks/')
;
gulpLoadPlugins.imagemin = require('gulp-imagemin');
gulpTaskList.forEach(function(taskfile) {
require('./gulp/tasks/' + taskfile)(gulp, gulpLoadPlugins, config);
});

image.js

1     
2
3
4
5
6
7
8
9
10
11
module.exports = function (gulp, Plugin, config) {     
gulp.task('img', function() {
return gulp.src('./images/**/*.*')
.pipe(Plugin.imagemin({
optimizationLevel: config.pnglevel,
progressive: true
}))
.pipe(gulp.dest('./imagemini'))
});
};

此次迭代结束后,我把子任务中通用的配置都写到 ./gulp/config.json 中,全局配置。

重构迭代4: 参数配置模块化

此次迭代紧跟迭代3,json 不够完美,不想每次去写 “”, 这里我把配置文件封装成一个模块
即迭代3 中的 config.json 变成了 config.js

config.js

1     
2
3
4
5
6
module.exports = function () {     
var config = {
pnglevel: 2
};
return config;
};

gulpfile.js 加载

1
var config = require('./gulp/gulp.config')();

其他不变,当封装成一个模块的时候,你就发现好处多多了,可以在模块中添加函数,你也可以把配置拆分,根据你的业务需要,自由调整。通过4步的迭代,整个代码组织架构就清晰多了。

注意: 子任务中注意文件夹的层次,子任务中的文件夹是以 gulpfile.js 为基准,因为 gulpfile.js 把子任务都包含进来了。

转载地址:http://gpeni.baihongyu.com/

你可能感兴趣的文章
ES7的Async/Await
查看>>
React Native WebView组件实现的BarCode(条形码)、(QRCode)二维码
查看>>
每个人都能做的网易云音乐[vue全家桶]
查看>>
JavaScript专题之数组去重
查看>>
Immutable.js 以及在 react+redux 项目中的实践
查看>>
Vue2.0全家桶仿腾讯课堂(移动端)
查看>>
React+Redux系列教程
查看>>
react-native 自定义倒计时按钮
查看>>
19 个 JavaScript 常用的简写技术
查看>>
ES6这些就够了
查看>>
微信小程序:支付系列专辑(开发指南+精品Demo)
查看>>
iOS应用间相互跳转
查看>>
iOS开发之支付宝集成
查看>>
iOS开发 支付之银联支付集成
查看>>
iOS开发支付集成之微信支付
查看>>
浅谈JavaScript--声明提升
查看>>
React非嵌套组件通信
查看>>
Websocket 使用指南
查看>>
浏览器兼容性问题解决方案 · 总结
查看>>
一个很棒的Flutter学习资源列表
查看>>