gulp 入门


- 第一步 各种安装

```
$ npm install --global gulp-cli  //global
// just in current node project
$ npm init
$ npm install --save-dev gulp

```

接着在当前的项目的根目录创建一个` gulpfile.js` 文件

```
var gulp = require('gulp');

gulp.task('default', function() {
  // place code for your default task here
  console.log('hello, glup');
});
```

接着执行:

> gulp

看看输出什么


语法很简单, 就是` gulp.task('task name', `function callback`)


```
% ./node_modules/gulp/bin/gulp.js
[15:48:32] Using gulpfile ~/workspace/gulp-demo1/gulpfile.js
[15:48:32] Starting 'default'...
hello, glup
[15:48:32] Finished 'default' after 167 μs
```

我们来看看` react` 的项目是如何使用` gulp`:

```react/gulpfile.js
'use strict';

var gulp = require('gulp');
var babel = require('gulp-babel');
var flatten = require('gulp-flatten');
var del = require('del');

var babelPluginModules = require('fbjs-scripts/babel-6/rewrite-modules');
var extractErrors = require('./scripts/error-codes/gulp-extract-errors');
var devExpressionWithCodes = require('./scripts/error-codes/dev-expression-with-codes');

// Load all of the Gulp plugins.
var plugins = require('gulp-load-plugins')();

function getTask(name) {
  return require(`./gulp/tasks/${name}`)(gulp, plugins);
}

var paths = {
  react: {
    src: [
      'src/**/*.js',
      '!src/**/__benchmarks__/**/*.js',
      '!src/**/__tests__/**/*.js',
      '!src/**/__mocks__/**/*.js',
      '!src/renderers/art/**/*.js',
      '!src/renderers/shared/fiber/**/*.js',
      '!src/shared/vendor/**/*.js',
    ],
    lib: 'build/modules',
  },
};

var moduleMap = Object.assign(
  {'object-assign': 'object-assign'},
  require('fbjs/module-map'),
  {
    deepDiffer: 'react-native/lib/deepDiffer',
    deepFreezeAndThrowOnMutationInDev: 'react-native/lib/deepFreezeAndThrowOnMutationInDev',
    flattenStyle: 'react-native/lib/flattenStyle',
    InitializeJavaScriptAppEngine: 'react-native/lib/InitializeJavaScriptAppEngine',
    RCTEventEmitter: 'react-native/lib/RCTEventEmitter',
    TextInputState: 'react-native/lib/TextInputState',
    UIManager: 'react-native/lib/UIManager',
    UIManagerStatTracker: 'react-native/lib/UIManagerStatTracker',
    View: 'react-native/lib/View',
  }
);

var errorCodeOpts = {
  errorMapFilePath: 'scripts/error-codes/codes.json',
};

var babelOpts = {
  plugins: [
    devExpressionWithCodes, // this pass has to run before `rewrite-modules`
    [babelPluginModules, {map: moduleMap}],
  ],
};
// 上面这些先略掉,就是定义一些东西了
// 主要还是直接看下面的task 定义

gulp.task('eslint', getTask('eslint'));

gulp.task('lint', ['eslint']);

gulp.task('flow', getTask('flow'));

gulp.task('version-check', getTask('version-check'));

// 通过gulp-load-plugins插件可以把 task 单独打包到不同的文件,然后任意加载.
// task 可以嵌套调用以的数组的形式作为参数传递.

// 通过这个插件删除 path 下的文件.
gulp.task('react:clean', function() {
  return del([paths.react.lib]);
});

gulp.task('react:modules', function() {
  return gulp
    .src(paths.react.src)
    .pipe(babel(babelOpts))
    .pipe(flatten())
    .pipe(gulp.dest(paths.react.lib));
});

gulp.task('react:extract-errors', function() {
  return gulp
    .src(paths.react.src)
    .pipe(extractErrors(errorCodeOpts));
});

gulp.task('default', ['react:modules']);

```

我们看出已经定义了

 - eslint
 - lint
 - flow
 - version-check
 - react:clean
 - react:modules
 - react:extract-errors
 - default


这两个 task 比较显示出` gulp`的` streaming` 了

```
gulp.task('react:modules', function() {
  return gulp
    .src(paths.react.src)
    .pipe(babel(babelOpts))
    .pipe(flatten())
    .pipe(gulp.dest(paths.react.lib));
});

gulp.task('react:extract-errors', function() {
  return gulp
    .src(paths.react.src)
    .pipe(extractErrors(errorCodeOpts));
});

```


上面有一段这样的:

```
gulp.src(globs, [options]).pipe()
          .pipe()
          ...
 
//①globs stirng or array of string
//②已经传递给 node-glob 的任意可接受参数         
```

[那就让我们来看看这个 api 吧](https://github.com/gulpjs/gulp/blob/master/docs/API.md)


```
gulp.src(globs[, options])

Emits files matching provided glob or an array of globs. Returns a stream of Vinyl files that can be piped to plugins.

```
意思就是通过` src()` 方法可以模式匹配文件,然后[返回` Vinyl` 文件](https://github.com/gulpjs/vinyl-fs)的流,接着通过` pipe` 的方式传递给下面的插件处理.


> 何为 vinyl file

说这个就得追到[这个vinyl](https://github.com/gulpjs/vinyl), 它是有 gulpjs 组织提供的一个虚拟的文件格式. 具体就是对于一个文件来说,通常只有路径位置和内容这样的属性, 而实际在使用中它还有很多其他附带的属性. 所以`vinyl` 就是用来描述一个文件的`元数据对象`. 而 vinyl adapter` 就是通过` vinyl` 这个描述来匹配返回数据. 而` vinyl file` 就是通过`src(某某条件)`而返回的匹配到的文件.

看看具体的一个例子:

```
var map = require('map-stream');
var vfs = require('vinyl-fs');

var log = function(file, cb) {
  console.log(file.path);
  cb(null, file);
};

vfs.src(['./js/**/*.js', '!./js/vendor/*.js'])
  .pipe(map(log))
  .pipe(vfs.dest('./output'));
  
```

> gulp.dest(path[, options])

这样 gulp 可以把文件流写到指定的目的地,然后紧接着把所有的数据作为流继续传递 pipe 给下面的插件.


``
gulp.src('./client/templates/*.jade')
  .pipe(jade())
  .pipe(gulp.dest('./build/templates'))
  .pipe(minify())
  .pipe(gulp.dest('./build/minified_templates'));
```

这里我们再回过头来看看最上面提到的`gulp.task()` 函数

```
gulp.task('somename', function() {
  // Do stuff
});
```

还有` watch` 方法, 它可以监视匹配的文件,一旦文件发生变化就执行.

```
gulp.watch(glob [, opts], tasks) or gulp.watch(glob [, opts, cb])

```