Gulp开发教程

Building With Gulp

===================css

原文地址
翻译出处

对网站资源进行优化,并使用不一样浏览器测试并非网站设计过程当中最有意思的部分,可是这个过程当中的不少重复的任务可以使用正确的工具自动完成,从而使效率大大提升,这是让不少开发者以为有趣的地方。html

Gulp是一个构建系统,它能经过自动执行常见任务,好比编译预处理CSS,压缩JavaScript和刷新浏览器,来改进网站开发的过程。经过本文,咱们将知道如何使用Gulp来改变开发流程,从而使开发更加快速高效。前端

What Is Gulp?

Gulp是一个构建系统,开发者可使用它在网站开发过程当中自动执行常见任务。Gulp是基于Node.js构建的,所以Gulp源文件和你用来定义任务的Gulp文件都被写进了JavaScript(或者CoffeeScript)里。前端开发工程师还能够用本身熟悉的语言来编写任务去lint JavaScript和CSS、解析模板以及在文件变更时编译LESS文件(固然这些只是一小部分例子)。node

Gulp自己虽然不能完成不少任务,但它有大量插件可用,开发者能够访问插件页面或者在npm搜索gulpplugin就能看到。例如,有些插件能够用来执行JSHint编译CoffeeScript执行Mocha测试,甚至更新版本号git

对比其余构建工具,好比Grunt,以及最近流行的Broccoli,我相信Gulp会更胜一筹(请看后面的”Why Gulp?”部分),同时我汇总了一个使用Javascript编写的构建工具清单,可供你们参考。github

Gulp是一个能够在GitHub上找到的开源项目。web

Installing Gulp

安装Gulp的过程十分简单。首先,须要在全局安装Gulp包:chrome

npm install -g gulp

 

而后,在项目里面安装Gulp:npm

npm install --save-dev gulp

 

Using Gulp

如今咱们建立一个Gulp任务来压缩JavaScript文件。首先建立一个名为gulpfile.js的文件,这是定义Gulp任务的地方,它能够经过gulp命令来运行,接着把下面的代码放到gulpfile.js文件里面。json

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

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

 

而后在npm里面运行npm install -–save-dev gulp-uglify来安装gulp-uglify,最后经过运行gulp minify来执行任务。假设js目录下有个app.js文件,那么一个新的app.js将被建立在编译目录下,它包含了js/app.js的压缩内容。想想,到底发生了什么?

咱们只在gulpfile.js里作了一点事情。首先,咱们加载gulp和gulp-uglify模块:

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

 

而后,咱们定义了一个叫minify的任务,它执行时会调用函数,这个函数会做为第二个参数:

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

});

 

最后,也是难点所在,咱们须要定义任务应该作什么:

gulp.src('js/app.js')
   .pipe(uglify())
   .pipe(gulp.dest('build'))

 

若是你对数据流很是熟悉(其实大多数前端开发人员并不熟悉),上面所提供的代码对你来讲就没有太大意义了。

STREAMS

数据流可以经过一系列的小函数来传递数据,这些函数会对数据进行修改,而后把修改后的数据传递给下一个函数。

在上面的例子中,gulp.src()函数用字符串匹配一个文件或者文件的编号(被称为“glob”),而后建立一个对象流来表明这些文件,接着传递给uglify()函数,它接受文件对象以后返回有新压缩源文件的文件对象,最后那些输出的文件被输入gulp.dest()函数,并保存下来。

整个数据流动过程以下图所示: 

当只有一个任务的时候,函数并不会起太大的做用。然而,仔细思考下面的代码:

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'));
});

 

在运行这段程序以前,你须要先安装gulp,gulp-jshint,gulp-uglify和gulp-concat。

这个任务会让全部的文件匹配js/*.js(好比js目录下的全部JavaScript文件),而且执行JSHint,而后打印输出结果,取消文件缩进,最后把他们合并起来,保存为build/app.js,整个过程以下图所示: 

若是你对Grunt 足够熟悉,就会注意到,Gulp和Grunt的工做方式很不同。Grunt不使用数据流,而是使用文件,对文件执行单个任务而后保存到新的文件中,每一个任务都会重复执行全部进程,文件系统频繁的处理任务会致使Grunt的运行速度比Gulp慢。

若是想要获取更加全面的数据流知识,请查看“Stream Handbook”.

GULP.SRC()

gulp.src()方法输入一个glob(好比匹配一个或多个文件的字符串)或者glob数组,而后返回一个能够传递给插件的数据流。

Gulp使用node-glob来从你指定的glob里面获取文件,这里列举下面的例子来阐述,方便你们理解:

  • js/app.js 精确匹配文件
  • js/*.js 仅匹配js目录下的全部后缀为.js的文件
  • js/*/.js 匹配js目录及其子目录下全部后缀为.js的文件
  • !js/app.js 从匹配结果中排除js/app.js,这种方法在你想要匹配除了特殊文件以外的全部文件时很是管用
  • *.+(js|css) 匹配根目录下全部后缀为.js或者.css的文件

此外,Gulp也有不少其余的特征,但并不经常使用。若是你想了解更多的特征,请查看Minimatch文档。

js目录下包含了压缩和未压缩的JavaScript文件,如今咱们想要建立一个任务来压缩尚未被压缩的文件,咱们须要先匹配目录下全部的JavaScript文件,而后排除后缀为.min.js的文件:

gulp.src(['js/**/*.js', '!js/**/*.min.js'])

 

DEFINING TASKS

gulp.task()函数一般会被用来定义任务。当你定义一个简单的任务时,须要传入任务名字和执行函数两个属性。

gulp.task('greet', function () {
   console.log('Hello world!');
});

 

执行gulp greet的结果就是在控制台上打印出“Hello world”.

一个任务有时也能够是一系列任务。假设要定义一个任务build来执行css、js、imgs这三个任务,咱们能够经过指定一个任务数组而不是函数来完成。

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

 

这些任务不是同时进行的,因此你不能认为在js任务开始的时候css任务已经结束了,也可能尚未结束。为了确保一个任务在另外一个任务执行前已经结束,能够将函数和任务数组结合起来指定其依赖关系。例如,定义一个css任务,在执行前须要检查greet任务是否已经执行完毕,这样作就是可行的:

gulp.task('css', ['greet'], function () {
   // Deal with CSS here
});

 

如今,当执行css任务时,Gulp会先执行greet任务,而后在它结束后再调用你定义的函数。

DEFAULT TASKS

你能够定义一个在gulp开始运行时候默认执行的任务,并将这个任务命名为“default”:

gulp.task('default', function () {
   // Your default task
});

 

PLUGINS

Gulp上有超过600种插件供你选择,你能够在插件页面或者npm上搜索gulpplugin来浏览插件列表。有些拥有“gulpfriendly”标签的插件,他们不能算插件,可是能在Gulp上正常运行。 须要注意的是,当直接在npm里搜索时,你没法知道某一插件是否在黑名单上(你须要滚动到插件页面底部才能看到)。

大多数插件的使用都很方便,它们都配有详细的文档,并且调用方法也相同(经过传递文件对象流给它),它们一般会对这些文件进行修改(可是有一些插件例外,好比validators),最后返回新的文件给下一个插件。

让咱们用前面的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'));
});

 

这里使用了三个插件,gulp-jshint,gulp-uglifygulp-concat。开发者能够参考插件的README文档,插件有不少配置选项,并且给定的初始值一般能知足需求。细心的读者可能会发现,程序中JSHint插件执行了2次,这是由于第一次执行JSHint只是给文件对象附加了jshint属性,并无输出。你能够本身读取jshint的属性或者传递给默认的JSHint的接收函数或者其余的接收函数,好比jshint-stylish.

其余两个插件的做用很清楚:uglify()函数压缩代码,concat(‘app.js’)函数将全部文件合并到一个叫app.js的文件中。

GULP-LOAD-PLUGINS

我发现gulp-load-plugin模块十分有用,它可以自动地从package.json中加载任意Gulp插件而后把它们附加到一个对象上。它的基本用法以下所示:

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

 

你能够把全部代码写到一行,可是我并不推荐这样作。

在执行那些代码以后,插件对象就已经包含了插件,并使用“驼峰式”的方式进行命名(例如,gulp-ruby-sass将被加载成plugins.rubySass),这样就能够很方便地使用了。例如,前面的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'));
});

 

假设package.json文件以下面所示:

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

 

这个例子虽然已经够短了,可是使用更长更复杂的Gulp文件会把它们简化成一两行代码。

三月初发布的Gulp-load-plugins0.4.0版本添加了延迟加载功能,提升了插件的性能,由于插件在使用的时候才会被加载进来,你不用担忧package.json里未被使用的插件影响性能(可是你须要把他们清理掉)。换句话说,若是你在执行任务时只须要两个插件,那么其余不相关的插件就不会被加载。

WATCHING FILES

Gulp能够监听文件的修改动态,而后在文件被改动的时候执行一个或多个任务。这个特性十分有用(对我来讲,这多是Gulp中最有用的一个功能)。你能够保存LESS文件,接着Gulp会自动把它转换为CSS文件并更新浏览器。

使用gulp.watch()方法能够监听文件,它接受一个glob或者glob数组(和gulp.src()同样)以及一个任务数组来执行回调。

让咱们看看下面,build任务能够将模板转换成html格式,而后咱们但愿定义一个watch任务来监听模板文件的变化,并将这些模板转换成html格式。watch函数的使用方法以下所示:

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

 

如今,当改变一个模板文件时,build任务会被执行并生成HTML文件,也能够给watch函数一个回调函数,而不是一个任务数组。在这个示例中,回调函数有一个包含触发回调函数信息的event对象:

gulp.watch('templates/*.tmpl.html', function (event) {
   console.log('Event type: ' + event.type); // added, changed, or deleted
   console.log('Event path: ' + event.path); // The path of the modified file
});

 

Gulp.watch()的另外一个很是好的特性是返回咱们熟知的watcher。利用watcher来监听额外的事件或者向watch中添加文件。例如,在执行一系列任务和调用一个函数时,你就能够在返回的watcher中添加监听change事件:

var watcher = gulp.watch('templates/*.tmpl.html', ['build']);
watcher.on('change', function (event) {
   console.log('Event type: ' + event.type); // added, changed, or deleted
   console.log('Event path: ' + event.path); // The path of the modified file
});

 

除了change事件,还能够监听不少其余的事件:

  • end 在watcher结束时触发(这意味着,在文件改变的时候,任务或者回调不会执行)
  • error 在出现error时触发
  • ready 在文件被找到并正被监听时触发
  • nomatch 在glob没有匹配到任何文件时触发

Watcher对象也包含了一些能够调用的方法:

  • watcher.end() 中止watcher(以便中止执行后面的任务或者回调函数)
  • watcher.files() 返回watcher监听的文件列表
  • watcher.add(glob) 将与指定glob相匹配的文件添加到watcher(也接受可选的回调当第二个参数)
  • watcher.remove(filepath) 从watcher中移除个别文件

Reloading Changes In The Browser

当一个文件被修改或者Gulp任务被执行时能够用Gulp来加载或者更新网页。LiveReload和BrowserSync插件就能够用来实如今游览器中加载更新的内容。

LIVERELOAD

LiveReload结合了浏览器扩展(包括Chrome extension),在发现文件被修改时会实时更新网页。它能够和gulp-watch插件或者前面描述的gulp-watch()函数一块儿使用。下面有一个gulp-livereload仓库中的README文件提到的例子:

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/*.less相匹配的文件的变化。一旦监测到变化,就会生成css并保存,而后从新加载网页.

BROWSERSYNC

BroserSync在浏览器中展现变化的功能与LiveReload很是类似,可是它有更多的功能。

当你改变代码的时候,BrowserSync会从新加载页面,或者若是是css文件,会直接添加进css中,页面并不须要再次刷新。这项功能在网站是禁止刷新的时候是颇有用的。假设你正在开发单页应用的第4页,刷新页面就会致使你回到开始页。使用LiveReload的话,你就须要在每次改变代码以后还须要点击四次,而当你修改CSS时,插入一些变化时,BrowserSync会直接将须要修改的地方添加进CSS,就不用再点击回退。

BrowserSync提供了一种在多个浏览器里测试网页的很好方式(查看大图)

BrowserSync也能够在不一样浏览器之间同步点击翻页、表单操做、滚动位置。你能够在电脑和iPhone上打开不一样的浏览器而后进行操做。全部设备上的连接将会随之变化,当你向下滚动页面时,全部设备上页面都会向下滚动(一般还很流畅!)。当你在表单中输入文本时,每一个窗口都会有输入。当你不想要这种行为时,也能够把这个功能关闭。

BrowserSync不须要使用浏览器插件,由于它自己就能够给你提供文件。(查看大图)

BrowserSync不须要使用浏览器插件,由于它自己就能够为你提供文件服务(若是文件是动态的,则为他们提供代理服务)和用来开启浏览器和服务器之间的socket的脚本服务。到目前为止这个功能的使用都十分顺畅。

实际上BrowserSync对于Gulp并不算一种插件,由于BrowserSync并不像一个插件同样操做文件。然而,npm上的BrowserSync模块能在Gulp上被直接调用。

首先,须要经过npm安装一下:

npm install --save-dev browser-sync

而后gulpfile.js会启动BrowserSync并监听文件:

var gulp = require('gulp'),
    browserSync = require('browser-sync');

gulp.task('browser-sync', function () {
   var files = [
      'app/**/*.html',
      'app/assets/css/**/*.css',
      'app/assets/imgs/**/*.png',
      'app/assets/js/**/*.js'
   ];

   browserSync.init(files, {
      server: {
         baseDir: './app'
      }
   });
});

 

执行gulp browser-sync后会监听匹配文件的变化,同时为app目录提供文件服务。

此外BrowserSync的开发者还写了不少关于BrowserSync+Gulp仓库的其余用途。

Why Gulp?

前面提到过,Gulp是为数很少的使用JavaScript开发的构建工具之一,也有其余不是用JavaScript开发的构建工具,好比Rake,那么咱们为何要选择Gulp呢?

目前最流行的两种使用JavaScript开发的构建工具是Grunt和Gulp。Grunt在2013年很是流行,由于它完全改变了许多人开发网站的方式,它有上千种插件可供用户使用,从linting、压缩、合并代码到使用Bower安装程序包,启动Express服务都能办到。这些和Gulp的很不同,Gulp只有执行单个小任务来处理文件的插件,由于任务都是JavaScript(和Grunt使用的大型对象不一样),根本不须要插件,你只需用传统方法启动一个Express服务就能够了。

Grunt任务拥有大量的配置,会引用大量你实际上并不须要的对象属性,可是Gulp里一样的任务也许只有几行。让咱们看个简单的Gruntfile.js,它规定一个将LESS转换为CSS的任务,而后执行Autoprefixer:

grunt.initConfig({
   less: {
      development: {
         files: {
            "build/tmp/app.css": "assets/app.less"
         }
      }
   },

   autoprefixer: {
      options: {
         browsers: ['last 2 version', 'ie 8', 'ie 9']
      },
      multiple_files: {
         expand: true,
         flatten: true,
         src: 'build/tmp/app.css',
         dest: 'build/'
      }
   }
});

grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-autoprefixer');

grunt.registerTask('css', ['less', 'autoprefixer']);

 

与Gulpfile.js文件进行对比,它们执行的任务相同:

var gulp = require('gulp'),
   less = require('gulp-less'),
   autoprefix = require('gulp-autoprefixer');

gulp.task('css', function () {
   gulp.src('assets/app.less')
      .pipe(less())
      .pipe(autoprefix('last 2 version', 'ie 8', 'ie 9'))
      .pipe(gulp.dest('build'));
});

 

由于Grunt比Gulp更加频繁地操做文件系统,因此使用数据流的Gulp老是比Grunt快。对于一个小的LESS文件,gulpfile.js一般须要6ms,而gruntfile.js则须要大概50ms——慢8倍多。这只是个简单的例子,对于长的文件,这个数字会增长得更显著。

相关文章
相关标签/搜索