[关闭]
@bornkiller 2018-07-18T16:16:15.000000Z 字数 3953 阅读 4343

gulp plugin编写

前端工具


前言

前端开发近两年工程化大幅飙升。随着Nodejs大放异彩,静态文件处理不再需要其他语言辅助。主要的两大工具即为基于文件的grunt,基于流的gulp。简单来说,如果需要的只是文件处理,gulp绝对首选。如果是其他依赖于文件的任务管理,例如测试(karmamocha),推荐使用grunt

gulp plugin开发依赖

就插件开发难度而言,gulp远低于grunt。如果你只关注如何处理文件,而不关注细节,那么需要依赖Nodejs Transform stream的实现。可以使用官方推荐的through2,但推荐使用through-gulp。后者是基于前者,为gulp插件编写精简优化重写而来。千万不要使用through,这个包时间久远,长时间没有维护,而且部分mock实现的功能,到nodejs 0.10.x已经原生支持。如果只是想学习如何编写gulp插件,through-gulp更适合。
through-gulp: https://github.com/bornkiller/through-gulp
through2: https://github.com/rvagg/through2.git
through: https://github.com/dominictarr/through

gulp plugin开发结构

  1. // PLUGIN_NAME: sample
  2. var through = require('through-gulp');
  3. function sample() {
  4. // creating a stream through which each file will pass
  5. var stream = through(function(file, encoding,callback) {
  6. // do whatever necessary to process the file
  7. if (file.isNull()) {
  8. }
  9. if (file.isBuffer()) {
  10. }
  11. if (file.isStream()) {
  12. }
  13. // just pipe data next, or just do nothing to process file later in flushFunction
  14. // never forget callback to indicate that the file has been processed.
  15. this.push(file);
  16. callback();
  17. },function(callback) {
  18. // just pipe data next, just callback to indicate that the stream's over
  19. this.push(something);
  20. callback();
  21. });
  22. // returning the file stream
  23. return stream;
  24. };
  25. // exporting the plugin
  26. module.exports = sample;

then use the plugin with gulp

  1. var gulp = require('gulp');
  2. var sample = require('sample');
  3. gulp.task('sample', function() {
  4. gulp.src(['source file'])
  5. .pipe(sample())
  6. .pipe(gulp.dest('file destiny'))
  7. });

这个sample是一个plugin的基本模板,通常的内容处理包裹与如下所示部分,所以通常关注重点也在此处。。

  1. if (file.isBuffer()) {
  2. //文件处理
  3. }

需要特别注意的是,如果你需要处理的不同文件之间没有任何依赖,在第一个函数函数内部处理完后,进行如下调用,即可将该文件的处理结果传递给下一个插件。这种情况下,可以缺省第二个参数flushFunction

  1. if (file.isBuffer()) {
  2. // 文件处理
  3. var data = fileProcess(file);
  4. // 传递处理后数据给下一个插件
  5. this.push(data);
  6. // 声明该文件处理完毕
  7. callback();
  8. }

如果需要处理的不同文件之间存在依赖,例如文件合并,需要所有文件全部读完之后再处理,那么第一个参数transformFunction将每次传递进来的数据保存在内存中(绝对不要在这里调用this.push()方法),第二个参数flushFunction统一做处理后,再传递给下一个插件。

  1. // transformFunction
  2. var fileStorage = [];
  3. if (file.isBuffer()) {
  4. // 文件处理
  5. var data = fileProcess(file);
  6. // 保存传递进来的数据
  7. fileStorage.push(data);
  8. // 声明该文件处理完毕
  9. callback();
  10. }
  1. // flushFunction
  2. function(callback) {
  3. var result = '';
  4. var final = null;
  5. fileStorage.foreach(function(file, key) {
  6. result += file.contents.toString();
  7. })
  8. final = new Buffer(result);
  9. this.push(final);
  10. callback();
  11. }

gulp优势浅析

  1. // Gruntfile.js
  2. // 包装函数
  3. module.exports = function(grunt) {
  4. // 任务配置
  5. grunt.initConfig({
  6. pkg: grunt.file.readJSON('package.json'),
  7. coffee: {
  8. options: {
  9. bare: true,
  10. sourceMap: true
  11. },
  12. compile: {
  13. files: {
  14. 'storage-coffee/judge.js': ['storage-coffee-source/judge.coffee'],
  15. 'storage-coffee/storage.js': ['storage-coffee-source/storage.coffee']
  16. }
  17. }
  18. }
  19. // 任务加载
  20. grunt.loadNpmTasks('grunt-contrib-coffee');
  21. };
  1. // gulpfile.js
  2. var gulp = require('gulp');
  3. var gutil = require('gulp-util');
  4. var coffee = require('gulp-coffee');
  5. var sourcemaps = require('gulp-sourcemaps');
  6. gulp.task('coffee', function() {
  7. gulp.src('storage-coffee-source/*.coffee')
  8. .pipe(sourcemaps.init())
  9. .pipe(coffee({bare: true}).on('error', gutil.log))
  10. .pipe(sourcemaps.write('./'))
  11. .pipe(gulp.dest('storage-coffee'));
  12. });

后记

就插件编写的角度而言,through-gulp方法更加精炼,是为gulp插件开发而生,而不是为了node stream开发而生,所以无需理会through2晦涩的文档,之前有一个不幸的地方,在gulp插件开发的单元测试中,常用的assert-stream是基于through2的,但是不影响大局。现在基于through-gulp编写gulp插件测试的模块stream-assert-gulp,目的在于为gulp插件编写与测试降低难度。基于前两者,编写了gulp-requirejs-optimizer插件作为使用requirejs作为AMD加载器的优化工作,基本可用。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注