Gulp,新一代流行的任务自动管理工具

Gulp与Grunt一样,也是一个自动任务运行器。它充分借鉴了Unix操作系统的管道(pipe)思想,很多人认为,在操作上,它要比Grunt简单。

安装

Gulp需要全局安装,然后再在项目的开发目录中安装为本地模块。先进入项目目录,运行下面的命令。

npm install -g gulp

// 将gulp安装到项目本地
npm install --save-dev gulp
// 安装插件
npm install --save-dev gulp-uglify
npm install gulp-jshint gulp-sass gulp-concat gulp-uglify gulp-rename --save-dev

gulpfile.js

项目根目录中的gulpfile.js,是Gulp的配置文件。

var gulp = require('gulp');

gulp.task('default', function() {

});

下面就是一个典型的gulpfile.js文件。

var gulp = require('gulp'),
    uglify = require('gulp-uglify');

gulp.task('minify', function() {
    gulp.src('js/app.js')
        .pipe(uglify())
        .pipe(gulp.dest('build'))
});

gulp模块的task方法指定任务。task方法有两个参数,第一个是任务名,第二个是任务函数。

在任务函数中:

使用gulp模块的src方法,指定所要处理的文件,然后使用pipe方法,将上一步的输出转为当前的输入,进行链式处理。

从上面的例子中可以看到,gulp充分使用了“管道”思想,就是一个数据流(stream):src方法读入文件产生数据流,dest方法将数据流写入文件,中间是一些中间步骤,每一步都对数据流进行一些处理。

gulp.src('js/app.js'):指定所要处理的文件 pipe():将上一步的输出转为当前的输入,进行链式处理。 uglify():压缩源码 gulp.dest('build'):将上一步的输出写入本地文件,这里是build.js(代码中省略了后缀名js)。

// 引入 gulp
var gulp = require('gulp');

// 引入组件
var jshint = require('gulp-jshint');
var sass = require('gulp-sass');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');

// 检查脚本:Link任务会检查js/目录下得js文件有没有报错或警告。
gulp.task('lint', function() {
    gulp.src('./js/*.js')
        .pipe(jshint())
        .pipe(jshint.reporter('default'));
});

// 编译Sass:Sass任务会编译scss/目录下的scss文件,并把编译完成的css文件保存到/css目录中。
gulp.task('sass', function() {
    gulp.src('./scss/*.scss')
        .pipe(sass())
        .pipe(gulp.dest('./css'));
});

// 合并,压缩文件:scripts任务会合并js/目录下得所有得js文件并输出到dist/目录,然后gulp会重命名、压缩合并的文件,也输出到dist/目录。
gulp.task('scripts', function() {
    gulp.src('./js/*.js')
        .pipe(concat('all.js'))
        .pipe(gulp.dest('./dist'))
        .pipe(rename('all.min.js'))
        .pipe(uglify())
        .pipe(gulp.dest('./dist'));
});

// 默认任务:这时,我们创建了一个基于其他任务的default任务。使用.run()方法关联和运行我们上面定义的任务,使用.watch()方法去监听指定目录的文件变化,当有文件变化时,会运行回调定义的其他任务。
gulp.task('default', function() {
    gulp.run('lint', 'sass', 'scripts');

    // 监听文件变化
    gulp.watch('./js/*.js', function() {
        gulp.run('lint', 'sass', 'scripts');
    });
});

现在,回到命令行,可以直接运行gulp任务了 gulp 这将执行定义的default任务,换言之,这和以下的命令式同一个意思 gulp default

模块加载

1.gulpfile.js中一一加载模块

gulpfile.js

var gulp = require('gulp'),
    jshint = require('gulp-jshint'),
    uglify = require('gulp-uglify'),
    concat = require('gulp-concat');

gulp.task('js', function() {
    return gulp.src('js/*.js')
        .pipe(jshint())
        .pipe(jshint.reporter('default'))
        .pipe(uglify())
        .pipe(concat('app.js'))
        .pipe(gulp.dest('build'));
});

2.使用gulp-load-plugins自动载入所有已安装的模块

使用gulp-load-plugins模块,可以加载package.json文件中所有的gulp模块。

package.json

{
    "devDependencies": {
        "gulp-concat": "~2.2.0",
        "gulp-uglify": "~0.2.1",
        "gulp-jshint": "~1.5.1",
        "gulp": "~3.5.6"
    }
}

gulpfile.js

var gulp = require('gulp'),
    gulpLoadPlugins = require('gulp-load-plugins'),
    plugins = gulpLoadPlugins();

gulp.task('js', function() {
    return gulp.src('js/*.js')
        .pipe(plugins.jshint())
        .pipe(plugins.jshint.reporter('default'))
        .pipe(plugins.uglify())
        .pipe(plugins.concat('app.js'))
        .pipe(gulp.dest('build'));
});

gulp.src()

gulp模块的src方法,用于产生数据流。它的参数表示所要处理的文件,一般有以下几种形式。

js/app.js:指定确切的文件名。 js/.js:某个目录所有后缀名为js的文件。 js/\*/*.js:某个目录及其所有子目录中的所有后缀名为js的文件。 !js/app.js:除了js/app.js以外的所有文件。 *.+(js|css):匹配项目根目录下,所有后缀名为js或css的文件。 ['js/*/\.js', '!js/*/\.min.js']:还可以是一个数组,用来指定多个成员。

gulp.task()

gulp模块的task方法,用于定义具体的任务。它的第一个参数是任务名,第二个参数是任务函数

  • 非常简单的任务函数:
gulp.task('greet', function() {
    console.log('Hello world!');
});
  • task方法还可以指定按顺序运行的一组任务:
gulp.task('css', function() {
    console.log('css...');
});

gulp.task('js', function() {
    console.log('js...');
});

gulp.task('imgs', function() {
    console.log('imgs...');
});

gulp.task('build', ['css', 'js', 'imgs']);

上面代码先指定build任务,它按次序由css、js、imgs三个任务所组成。注意,由于每个任务都是异步调用,所以没有办法保证js任务的开始运行的时间,正是css任务运行结束。

  • 任务严格按次序运行:
gulp.task('greet', function() {
    console.log('Hello world!');
});

gulp.task('css', ['greet'], function() {
    console.log('css...');
});

gulp.task('js', function() {
    console.log('js...');
});

gulp.task('build', ['css', 'js']);

上面代码表明,css任务依赖greet任务,所以css一定会在greet运行完成后再运行。

  • 默认任务:
gulp.task('default', function() {
    // Your default task
});

如果一个任务的名字为default,就表明它是“默认任务”,在命令行直接输入gulp命令,就会运行该任务。

gulp.watch()

gulp模块的watch方法,用于指定需要监视的文件。一旦这些文件发生变动,就运行指定任务。

  • watch方法
gulp.task('watch', function() {
    gulp.watch('templates/*.tmpl.html', ['build']);
});

上面代码指定,一旦templates目录中的模板文件发生变化,就运行build任务。

  • watch方法也可以用回调函数,代替指定的任务。
gulp.watch('templates/*.tmpl.html', function(event) {
    console.log('Event type: ' + event.type);
    console.log('Event path: ' + event.path);
});
  • 另一种写法是watch方法所监控的文件发生变化时(修改、增加、删除文件),会触发change事件。可以对change事件指定回调函数。
var watcher = gulp.watch('templates/*.tmpl.html', ['build']);

watcher.on('change', function(event) {
    console.log('Event type: ' + event.type);
    console.log('Event path: ' + event.path);
});

除了change事件,watch方法还可能触发以下事件:

  • end:回调函数运行完毕时触发。
  • error:发生错误时触发。
  • ready:当开始监听文件时触发。
  • nomatch:没有匹配的监听文件时触发。
  • watcher对象还包含其他一些方法。
  • watcher.end():停止 watcher对象,不会再调用任务或回调函数。
  • watcher.files():返回 watcher对象监视的文件。
  • watcher.add(glob):增加所要监视的文件,它还可以附件第二个参数,表示回调函数。
  • watcher.remove(filepath):从 watcher对象中移走一个监视的文件。

模块

  • gulp-load-plugins模块(如上)

  • gulp-livereload:模块用于自动刷新浏览器,反映出源码的最新变化。它除了模块以外,还需要在浏览器中安装插件,用来配合源码变化。

var gulp = require('gulp'),
    less = require('gulp-less'),
    livereload = require('gulp-livereload'),
    watch = require('gulp-watch');

gulp.task('less', function() {
    gulp.src('less/*.less')
        .pipe(watch())
        .pipe(less())
        .pipe(gulp.dest('css'))
        .pipe(livereload());
});

上面代码监视less文件,一旦编译完成,就自动刷新浏览器。

自动载入(gulp-load-plugins) 编译Sass (gulp-ruby-sass) Autoprefixer (gulp-autoprefixer) 缩小化(minify)CSS (gulp-minify-css) JSHint (gulp-jshint) 拼接 (gulp-concat) 丑化(Uglify) (gulp-uglify) 图片压缩 (gulp-imagemin) 即时重整(LiveReload) (gulp-livereload) 清理档案 (gulp-clean) 图片快取,只有更改过得图片会进行压缩 (gulp-cache) 更动通知 (gulp-notify)

gulp-less : less gulp-ruby-sass : 支持sass gulp-minify-css : 压缩css gulp-jshint : 检查js gulp-uglify : 压缩js gulp-concat : 合并文件 gulp-rename : 重命名文件 gulp-htmlmin : 压缩html gulp-clean : 清空文件夹

执行下列指令来安装这些外挂:

$ npm install gulp-ruby-sass gulp-autoprefixer gulp-minify-css gulp-jshint gulp-concat gulp-uglify gulp-imagemin gulp-clean gulp-notify gulp-rename gulp-livereload gulp-cache --save-dev

指令将会安装必要的外挂,并纪录于package.json内的devDependencies物件。完整的gulp外挂清单可以在这裡找到。 fhttp://gratimax.net/search-gulp-plugins

配置事例

示例配置:Gulp 实现完整的 SASS 自动编译并刷新网页

// Include gulp
var gulp = require('gulp');

// Include Our Plugins
var sass = require('gulp-sass');

var lr = require('tiny-lr'),
    refresh = require('gulp-livereload'),
    server = lr();


// Task SASS
gulp.task('sass', function() {
    gulp.src([
        'scss/**/*.scss',
        '!scss/**/_*.scss'
    ])
        .pipe(sass({
            includePaths: ['scss']
        }))
        .pipe(gulp.dest('css'))
        .pipe(refresh(server));
});


gulp.task('refresh', function() {
    gulp.src([
        '**/*.html',
        '**/*.php'
    ])
        .pipe(refresh(server));
});


// Task: default
gulp.task('default', function() {
    gulp.run('sass');

    server.listen(35729, function(error) {
        if (error) return console.log(error);

        gulp.watch([
            'scss/**',
            'img/**'
        ], function(event) {
            gulp.run('sass');
        });

        gulp.watch([
            '**/*.html',
            '**.php'
        ], function(event) {
            gulp.run('refresh');
        });
    });
});

gulpfile.js

var gulp = require('gulp');

// 引入组件
var less = require('gulp-less'), // less
  minifycss = require('gulp-minify-css'), // CSS压缩
  uglify = require('gulp-uglify'), // js压缩
  concat = require('gulp-concat'), // 合并文件
  rename = require('gulp-rename'), // 重命名
  clean = require('gulp-clean'); //清空文件夹

// less解析
gulp.task('build-less', function() {
  gulp.src('./javis/static/less/lib/s-production.less')
    .pipe(less())
    .pipe(gulp.dest('./javis/static/build/css/lib/'))

  gulp.src('./javis/static/less/lib/s-skins.less')
    .pipe(less())
    .pipe(gulp.dest('./javis/static/build/css/lib/'))

  gulp.src('./javis/static/less/lib/s/s.less')
    .pipe(less())
    .pipe(gulp.dest('./javis/static/build/css/lib/'))

  gulp.src('./javis/static/less/*.less')
    .pipe(less())
    .pipe(gulp.dest('./javis/static/build/css/'))
});

// 合并、压缩、重命名css
gulp.task('stylesheets', ['build-less'], function() {
  // 注意这里通过数组的方式写入两个地址,仔细看第一个地址是css目录下的全部css文件,第二个地址是css目录下的areaMap.css文件,但是它前面加了!,这个和.gitignore的写法类似,就是排除掉这个文件.
  gulp.src(['./javis/static/build/css/*.css', '!./javis/static/build/css/areaMap.css'])
    .pipe(concat('all.css'))
    .pipe(gulp.dest('./javis/static/build/css/'))
    .pipe(rename({
      suffix: '.min'
    }))
    .pipe(minifycss())
    .pipe(gulp.dest('./javis/static/build/css'));
});

// 合并,压缩js文件
gulp.task('javascripts', function() {
  gulp.src('./javis/static/js/*.js')
    .pipe(concat('all.js'))
    .pipe(gulp.dest('./javis/static/build/js'))
    .pipe(rename({
      suffix: '.min'
    }))
    .pipe(uglify())
    .pipe(gulp.dest('./javis/static/build/js'));
});

// 清空图片、样式、js
gulp.task('clean', function() {
  return gulp.src(['./javis/static/build/css/all.css', './javis/static/build/css/all.min.css'], {
    read: false
  })
    .pipe(clean({
      force: true
    }));
});

// 将bower的库文件对应到指定位置
gulp.task('buildlib', function() {

  gulp.src('./bower_components/angular/angular.min.js')
    .pipe(gulp.dest('./javis/static/build/js/'))

  gulp.src('./bower_components/angular/angular.js')
    .pipe(gulp.dest('./javis/static/build/js/'))

  gulp.src('./bower_components/bootstrap/dist/js/bootstrap.min.js')
    .pipe(gulp.dest('./javis/static/build/js/'))

  gulp.src('./bower_components/jquery/dist/jquery.min.js')
    .pipe(gulp.dest('./javis/static/build/js/'))

  gulp.src('./bower_components/angular-route/angular-route.min.js')
    .pipe(gulp.dest('./javis/static/build/js/'))

  gulp.src('./bower_components/angular-animate/angular-animate.min.js')
    .pipe(gulp.dest('./javis/static/build/js/'))

  gulp.src('./bower_components/angular-bootstrap/ui-bootstrap.min.js')
    .pipe(gulp.dest('./javis/static/build/js/'))

  gulp.src('./bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js')
    .pipe(gulp.dest('./javis/static/build/js/'))

  //--------------------------css-------------------------------------

  gulp.src('./javis/static/less/fonts/*')
    .pipe(gulp.dest('./javis/static/build/css/fonts/'))

  gulp.src('./bower_components/bootstrap/fonts/*')
    .pipe(gulp.dest('./javis/static/build/css/fonts/'))

  gulp.src('./bower_components/bootstrap/dist/css/bootstrap.min.css')
    .pipe(gulp.dest('./javis/static/build/css/lib'))

});

// 定义develop任务在日常开发中使用
gulp.task('develop', function() {
  gulp.run('buildlib', 'build-less', 'javascripts', 'stylesheets');

  gulp.watch('./javis/static/less/*.less', ['build-less']);
});

// 定义一个prod任务作为发布或者运行时使用
gulp.task('prod', function() {
  gulp.run('buildlib', 'build-less', 'stylesheets', 'javascripts');

  // 监听.less文件,一旦有变化,立刻调用build-less任务执行
  gulp.watch('./javis/static/less/*.less', ['build-less']);
});

// gulp命令默认启动的就是default认为,这里将clean任务作为依赖,也就是先执行一次clean任务,流程再继续.
gulp.task('default', ['clean'], function() {
  gulp.run('develop');
});

gulp 与 Grunt

gulpfile.js

/*!
 * gulp
 * $ npm install gulp-ruby-sass gulp-autoprefixer gulp-minify-css gulp-jshint gulp-concat gulp-uglify gulp-imagemin gulp-notify gulp-rename gulp-livereload gulp-cache del --save-dev
 */

// Load plugins
var gulp = require('gulp'),
  sass = require('gulp-ruby-sass'),
  autoprefixer = require('gulp-autoprefixer'),
  minifycss = require('gulp-minify-css'),
  jshint = require('gulp-jshint'),
  uglify = require('gulp-uglify'),
  imagemin = require('gulp-imagemin'),
  rename = require('gulp-rename'),
  concat = require('gulp-concat'),
  notify = require('gulp-notify'),
  cache = require('gulp-cache'),
  livereload = require('gulp-livereload'),
  del = require('del');

// Styles
gulp.task('styles', function() {
  return gulp.src('src/styles/main.scss')
    .pipe(sass({
      style: 'expanded',
    }))
    .pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
    .pipe(gulp.dest('dist/styles'))
    .pipe(rename({
      suffix: '.min'
    }))
    .pipe(minifycss())
    .pipe(gulp.dest('dist/styles'))
    .pipe(notify({
      message: 'Styles task complete'
    }));
});

// Scripts
gulp.task('scripts', function() {
  return gulp.src('src/scripts/**/*.js')
    .pipe(jshint('.jshintrc'))
    .pipe(jshint.reporter('default'))
    .pipe(concat('main.js'))
    .pipe(gulp.dest('dist/scripts'))
    .pipe(rename({
      suffix: '.min'
    }))
    .pipe(uglify())
    .pipe(gulp.dest('dist/scripts'))
    .pipe(notify({
      message: 'Scripts task complete'
    }));
});

// Images
gulp.task('images', function() {
  return gulp.src('src/images/**/*')
    .pipe(cache(imagemin({
      optimizationLevel: 3,
      progressive: true,
      interlaced: true
    })))
    .pipe(gulp.dest('dist/images'))
    .pipe(notify({
      message: 'Images task complete'
    }));
});

// Clean
gulp.task('clean', function(cb) {
  del(['dist/assets/css', 'dist/assets/js', 'dist/assets/img'], cb)
});

// Default task
gulp.task('default', ['clean'], function() {
  gulp.start('styles', 'scripts', 'images');
});

// Watch
gulp.task('watch', function() {

  // Watch .scss files
  gulp.watch('src/styles/**/*.scss', ['styles']);

  // Watch .js files
  gulp.watch('src/scripts/**/*.js', ['scripts']);

  // Watch image files
  gulp.watch('src/images/**/*', ['images']);

  // Create LiveReload server
  livereload.listen();

  // Watch any files in dist/, reload on change
  gulp.watch(['dist/**']).on('change', livereload.changed);

});

Gruntfile.js


/*!
 * Grunt
 * $ npm install grunt-contrib-uglify grunt-autoprefixer grunt-contrib-cssmin grunt-contrib-imagemin grunt-contrib-sass grunt-contrib-watch grunt-contrib-concat grunt-contrib-clean grunt-contrib-jshint grunt-notify --save-dev
 */

module.exports = function(grunt) {

  grunt.initConfig({

    // Sass
    sass: {
      dist: {
        options: {
          style: 'expanded'
        },
        files: {
          'dist/styles/main.css': 'src/styles/main.scss'
        }
      }
    },

    // Autoprefix
    autoprefixer: {
      options: {
        browsers: [
          'last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'
        ]
      },
      dist: {
        src: 'dist/styles/main.css'
      }
    },

    // CSS minify
    cssmin: {
      dist: {
        files: {
          'dist/styles/main.min.css': 'dist/styles/main.css'
        }
      }
    },

    // JShint
    jshint: {
      files: ['src/scripts/**/*.js'],
      options: {
        jshintrc: '.jshintrc'
      }
    },

    // Concat
    concat: {
      js: {
        src: ['src/scripts/**/*.js'],
        dest: 'dist/scripts/main.js'
      },
    },

    // Uglify
    uglify: {
      dist: {
        src: 'dist/scripts/main.js',
        dest: 'dist/scripts/main.min.js'
      },
    },

    // Imagemin
    imagemin: {
      dist: {
        options: {
          optimizationLevel: 3,
          progressive: true,
          interlaced: true
        },
        files: [{
          expand: true,
          cwd: 'src/images',
          src: ['**/*.{png,jpg,gif}'],
          dest: 'dist/images'
        }]
      }
    },

    // Clean
    clean: {
      build: ['dist/styles', 'dist/scripts', 'dist/images']
    },

    // Notify
    notify: {
      styles: {
        options: {
          message: 'Styles task complete',
        }
      },
      scripts: {
        options: {
          message: 'Scripts task complete',
        }
      },
      images: {
        options: {
          message: 'Images task complete',
        }
      },
    },

    // Watch
    watch: {
      styles: {
        files: 'src/styles/**/*.scss',
        tasks: ['sass', 'autoprefixer', 'cssmin', 'notify:styles'],
      },
      scripts: {
        files: 'src/scripts/**/*.js',
        tasks: ['concat', 'uglify', 'notify:scripts'],
      },
      images: {
        files: 'src/images/**/*',
        tasks: ['imagemin', 'notify:images'],
      },
      livereload: {
        options: {
          livereload: true
        },
        files: [
          'dist/styles/**/*.css',
          'dist/scripts/**/*.js',
          'dist/images/**/*'
        ]
      }
    }
  });

  // Default task
  grunt.registerTask('default', [
    'jshint',
    'clean',
    'concat',
    'uglify',
    'sass',
    'autoprefixer',
    'cssmin',
    'imagemin'
  ]);

  // Load plugins
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-autoprefixer');
  grunt.loadNpmTasks('grunt-contrib-cssmin');
  grunt.loadNpmTasks('grunt-contrib-sass');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-imagemin');
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-notify');

};