Gulp打包工具

12 Jan

前端打包工具有很多款,从早期雅虎的Ant到现在的Webpack,期间的迭代变化的差别之大,如同Java到JavaScript,除了名字有点一样,几乎就是两门完全不同的语言。公司项目主要用Gulp和Webpack,本篇先整理一下Gulp

如果你用过Grunt,可能觉得Gulp有点莫名其妙,Grunt前辈社区完善,插件成千上万,弄出个Gulp干什么?我个人感觉在未来的前端开发中一切都将是JS,因此即便是配置工具,Gulp用JS代码实现的策略优于Grunt的半JS半配置的策略。而且Gulp用流的方式进行文件处理,通过管道将多个任务和操作连接在一起,全程只有一个IO过程,最后的结果直接写入硬盘。因此打包流程更加清晰,没有中间临时文件,速度更快。注意,不要轻视“速度更快”这个评价,打包的时间越短越好,快1秒也能提高前端工程师的效率,做前端的都懂的…

  • 安装和执行
  • 插件引入
  • task
  • 数据流
  • watch

安装和执行

安装很简单:

npm install --global gulp

安装之后,在工程目录下新建一个名为gulpfile.js的文件:

var gulp = require('gulp');
gulp.task('default', function() {
    ……
});

这样在目录下执行gulp就能自动执行上面定义的default方法。

插件引入

作为一款流程的打包工具,社区丰富的插件库是必不可少的。除了var gulp=require(‘gulp’);是一定要引入的外,其他的插件根据需要自行引入。例如gulpfile.js的头部可能是这样的:

var gulp = require('gulp');
var del = require('del');
var less = require('gulp-less');
var base64 = require('gulp-base64');
var tpl2mod = require('gulp-tpl2mod');
var extReplace = require('gulp-ext-replace');

从上面代码就能看出,Gulp完全遵守CommonJS的规范,无论是前端页面工程师,还是后端Node工程师都可以零基础立马上手。这可能也是Gulp能取代Grunt的一个原因。

task

引入插件后,就需要建立一个个子任务来执行插件。task函数的原型:

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

第一个参数是task名。第二个参数可选,是个任务列表array,这些任务会在运行当前任务之前运行,例如:

gulp.task('default', ['css', 'image', 'ejs', 'babel']);

需要注意的是,你需要确保array里的任务都在当前任务开始前被运行完毕。如果array里的任务是同步方法,那就没没什么好担心的,Gulp会确保顺序依次执行。但如果array里的任务是异步方法,需要确保是否使用了一个callback,或返回一个promise或stream。

第三个参数fn里定义了任务需要执行的操作。

数据流

如果你熟悉Java,那么IO数据流这个策略应该非常熟了。Gulp通过串联起来的的小函数来传递数据,这些函数会对数据进行修改,然后把修改后的数据传递给下一个函数。src函数的原型:

gulp.src(globs[, options])

该函数将输出匹配globs的文件。输出的文件可以用pipe输入到别的插件中,形成一串数据流。

第一个参数globs,可以是string也可以是array,是一些常用的正则表达式,例如:

js/sample.js	//精确匹配文件
js/*.js 	//匹配js目录下的所有js文件
js/**/*.js	//匹配js目录及其子目录下所有后缀为.js的文件
!js/sample.js	//从匹配结果中去除js/sample.js文件
*.+(js|css)	//匹配根目录下所有后缀为.js或者.css的文件

//例如:匹配js目录及其子目录下所有js文件,但需要去除后缀为.min.js的文件
gulp.src(['js/**/*.js', '!js/**/*.min.js'])

更多高级语法,请仔细阅读node-glob语法。

第二个参数options可选,除了支持node-globglob-stream的参数外,还支持一些Gulp的额外的参数。例如:

options.buffer:如果设为false,将会以stream方式返回file.contents而不是文件buffer的形式。这在处理一些大文件的时候将会很有用。

options.read:如果设为false, 那么file.contents会返回null,这样就不会去读取文件

options.base:string型的值,将会被加在glob之前。例如下面两段代码唯一的区别是,上面这段没有options.base,而下面这段加上了options.base,导致结果路径有区别:

gulp.src('client/js/**/*.js') 
  .pipe(minify())
  .pipe(gulp.dest('build')); 
 //匹配client/js/somedir/somefile.js并且将base解析为client/js/,因此结果 写入build/somedir/somefile.js

gulp.src('client/js/**/*.js', { base: 'client' })
  .pipe(minify())
  .pipe(gulp.dest('build'));
//结果写入build/js/somedir/somefile.js

数据流的结果通过dest函数输出,原型:

gulp.dest(path[, options])

第一个参数是文件被输出的路径,路径是相对路径,当然相对路径也可根据上面的options.base来计算。第二个参数options可选,具体可以参照Linux的文件系统操作命令:

options.cwd:默认值process.cwd()。输出目录的 cwd 参数,只在所给的输出目录是相对路径时候有效。

options.mode:默认值0777,用以定义所有在输出目录中所创建的目录的权限。

看一个Babel解码ES6的例子:

gulp.task('babel', function () {
    return gulp.src('./javascript/**/*.js')
        .pipe(babel({
            presets: [
                'es2015',
                'stage-0'
            ],
            plugins: [
                'transform-runtime',
                'syntax-async-functions',
                'transform-class-properties',
                'transform-decorators-legacy'
            ]
        }))
        .pipe(gulp.dest('./js'));
});

最后,Gulp及其插件使用的流是vinyl文件对象流(stream或buffer),和普通的文件流(chunk)还是有区别的。因此在使用Gulp时,如果pipe出现Streaming not supported。可能是因为传给Gulp及其插件的是普通文件流(例如Node里fs.createReadStream(‘app.js’).pipe(uglify()))。可以用vinyl-source-stream转换工具转一下就行了。

watch

Gulp可以监视文件的修改,功能类似于Jade里的-w参数,当文件有变动时,执行回调函数。Watch有两种原型:

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

第一种原型:第一个参数glob用于指定哪些文件需要被监视,语法参数数据流不赘述。第二个参数opts可选,参照gaze。第三个参数tasks用于指定当被监视的文件有改动时,执行哪些task。watch函数始终会返回一个EventEmitter 来发射change 事件。例如:

var watcher = gulp.watch('js/**/*.js', ['uglify','reload']);
watcher.on('change', function(event) {
  console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
});

第二种原型:和第一种的区别是,第三个参数不是task数组,而是自定义回调函数。回调函数的参数是event对象,你可以从该参数中获取到event.type(被监视的文件发生的变化类型added,changed, deleted)和event.path(触发该事件的文件的路径)例如:

gulp.watch('js/**/*.js', function(event) {
  console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
});

总结

技术总是在发展,从实际使用下来的经验看,Gulp确实比Grunt更快,代码更少更易读。Gulp能火多久不清楚,目前来看,还是项目的一个比较理想的打包工具。

Leave a Reply

Your email address will not be published. Required fields are marked *