首页 > 代码库 > gulp初探

gulp初探

gulp初探

gulp是基于node.js的一个前端构建系统。它能通过自己主动运行常见任务,比方编译预处理CSS,压缩JavaScript和刷新浏览器。来改进站点的开发流程

gulp安装
1.假设没有安装node.js。请先去node管网安装最新版node.js;
2.安装了node后,在全局安装gulp;
   npm install -g gulp
3.然后。在项目里安装Gulp;
     npm install --save-dev gulp

Gulp使用
如今我们创建一个Gulp任务来压缩JavaScript文件。首先创建一个名为gulpfile.js的文件,这是定义Gulp任务的地方,它能够通过gulp命令来执行,接着把以下的代码放到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‘))});
然后在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()

ulp.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不须要使用浏览器插件,由于它本身就能够为你提供文件服务(假设文件是动态的。则为他们提供代理服务)和用来开启浏览器和server之间的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倍多。这仅仅是个简单的样例,对于长的文件。这个数字会添加得更显著。

gulp初探