曾经,有一个 gulp-browserify 摆在我面前,后来它被加入了黑名单

按照 CommonJS 规范开发浏览器端应用越来越流行,这过程中 Browserify 功不可没。而 gulp 作为当下最流行的 JavaScript 构建工具,browserify 免不了要它发生关系 :stuck_out_tongue:。

社区为 gulp 开发了各种各样的插件,但 gulp 生态系统是有逼格的,不是来者不拒。对于一些不符合 gulp 理念的插件,被毫不留情地加入了黑名单,gulp-browserify 便是其中之一。

gulp-browserify 对新手来说还是挺方便,现在每周还有近 5k 的下载量。如果你喜欢,还是可以继续使用,只是不再有人维护和更新。

本文要说的,正是离开 gulp-browserify 这个中间人后,如何在 gulp 中使用 browserify。其实在上一篇文章中,已经详细解释了背后的原理,本文只是对使用例子做进一步整理。

要点:Stream 转换

vinyl-source-stream + vinyl-buffer

var browserify = require('browserify');
var gulp = require('gulp');
var uglify = require('gulp-uglify');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');

gulp.task('browserify', function() {
  return browserify('./src/js/app.js')
    .bundle()
    .pipe(source('bundle.js')) // gives streaming vinyl file object
    .pipe(buffer()) // <----- convert from streaming to buffered vinyl file object
    .pipe(uglify()) // now gulp-uglify works 
    .pipe(gulp.dest('./dist/js'));
});

其中,vinyl-buffer 这一步可以使用 gulp-stream 或者 gulp-streamify 替代,解决插件不支持 stream 的问题。

var browserify = require('browserify');
var gulp = require('gulp');
var uglify = require('gulp-uglify');
var source = require('vinyl-source-stream');
var streamify = require('gulp-streamify');

gulp.task('browserify', function() {
  return browserify('./src/js/app.js')
    .bundle()
    .pipe(source('bundle.js')) // gives streaming vinyl file object
    .pipe(buffer())
    .pipe(streamify(uglify())) // let gulp-uglify work with stream 
    .pipe(gulp.dest('./dist/js'));
});

through2

在 gulp 管道中使用 throuth2 操作 vinyl 文件对象,browserify 处理以后再返回管道中。

var browserify = require('browserify');
var gulp = require('gulp');
var uglify = require('gulp-uglify');
var through2 = require('through2');

gulp.task('browserify', function() {
  return gulp.src('./src/js/app.js')
    .pipe(through2.obj(function(file, enc, next) {
      browserify(file.path)
        // .transform(reactify)
        .bundle(function(err, res) {
          err && console.log(err.stack);
          file.contents = res;
          next(null, file);
        });
    }))
    .pipe(uglify()) // uglify
    .pipe(gulp.dest('./dist/js'));
});

这其实和使用 vinyl-transform 是一个原理,只是从 browserify 9.x 某个版本开始,下面的代码不再工作,原因未知,因此不建议使用。

var gulp = require('gulp');
var browserify = require('browserify');
var transform = require('vinyl-transform');
var uglify = require('gulp-uglify');

gulp.task('browserify', function () {
  var browserified = transform(function(filename) {
    var b = browserify(filename);
    return b.bundle();
  });
  return gulp.src(['./src/app.js'])
    .pipe(browserified)
    .pipe(uglify())
    .pipe(gulp.dest('./dist'));
});

其他任务

把常规流转转成 vinyl 对象流是在 gulp 中直接使用 browserify 的关键点,**理解了这点,所有问题都迎刃而解了,其他操作也是在此基础上进行的。

多文件操作

有时我们需要把多个文件添加到 browserify 中,可以借助 node-glob 这个模块实现:

var gulp = require('gulp');
var browserify = require('browserify');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var glob = require('node-glob');
var uglify = require('gulp-uglify');

gulp.task('browserify', function(cb) {
  glob('./src/**/*.js', {}, function(err, files) {
    var b = browserify();
    files.forEach(function(file) {
      b.add(file);
    });

    b.bundle()
      .pipe(source('output.js'))
      .pipe(buffer())
      .pipe(uglify())
      .pipe(gulp.dest('./dist'));

    cb();
  });
});

当然,也可以继续使用 gulp.src('./src/**/*.js'),然后使用 through2 处理。

使用 Watchify 提高速度

当依赖的文件很多时,browserify 处理速度会很慢,使用 watchify 可以大幅提高处理速度。

var watchify = require('watchify');
var browserify = require('browserify');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var gutil = require('gulp-util');
var uglify = require('gulp-uglify');

// add custom browserify options here
var b = watchify(browserify(assign({}, watchify.args, {
  cache: {}, // required for watchify
  packageCache: {}, // required for watchify
  entries: ['./src/index.js']
}))); 

// add transformations here
// i.e. b.transform(coffeeify);

gulp.task('browserify', bundle); 
b.on('update', bundle); // on any dep update, runs the bundler
b.on('log', gutil.log); // output build logs to terminal

function bundle() {
  return b.bundle()
    // log errors if they happen
    .on('error', gutil.log.bind(gutil, 'Browserify Error'))
    .pipe(source('bundle.js'))
    // optional, remove if you don't need to buffer file contents
    .pipe(buffer())
    .pipe(uglify())
    .pipe(gulp.dest('./dist'));
}

相关参考