gulp之道

文章同步自個人github,地址:https://github.com/ireeoo/not...,已於2018年1月17號從新編輯。javascript

gulp

提及來用 gulp也有四年多了,以前在 sf上也寫過一篇文章,今天就從新整理下相關的東西,跟你們分享下,長文預警~

gulp是什麼

__上面這個大可樂就是了__~css

gulp是一個___基於流式構建___的TaskRunner,經過gulp你能夠本身編寫一系列的gulp任務,組成一個完整的構建系統,幫你完成大部分無聊的工做,這也是構建工具最大的價值 —— 自動化。html

說到gulp就很難避開gruntgrunt是先於gulp出現的TaskRunner,只不過gulp優化了一些功能的實現方式,帶來了不同的思想。前端

gulp vs grunt

先說一下gulpvue

  1. gulp是經過正常的寫代碼而不是配置的方式來實現構建。
  2. gulp採用了unix的管道思想,把上一級的輸出當作下一級的輸入,把簡單的一些gulp插件組合起來,解決複雜的問題,每一個插件只負責本身的事情,完成本身的工做以後就把流扔給下一個插件,下一個插件處理完以後再往下傳,如此,直至處理完,而後生成最後的處理結果,整個過程,採用了流式的寫法.
  3. gulp只抽象出了不多的API,很容易上手。
  4. gulp基於nodejsstream來處理文件,不會產生中間文件,能更快地完成構建。

gulp處理任務:
gulpjava

再看看gruntnode

  1. grunt中,一切都是基於配置的,在一個大型的構建系統中,你可能會看到比較多的配置項,一堆配置可能看得人頭暈眼花,每一個插件的配置項還不盡相同,用一個插件你就得取熟悉下它的配置,這就很噁心了。
  2. grunt是基於文件系統來構建的,在構建的過程當中會生成中間文件,A插件處理完的東西不能直接交給B插件繼續處理,而是採用了迂迴的方式,A把處理後的扔到C這個地方,而後B插件從C這個地方再去讀取,影響構建速度卻是小事,主要是把事情搞複雜了。聽說grunt後續的版本可能也會使用stream這種形式,其實這也不奇怪,看看如今的webpack,前端時間rolluptree-shaking爲突破口,準備挑戰webpack,結果webpack分分鐘就把tree-shaking的特性給實(chao)現(xi)了,你有的老子都有,你沒有的老子還有,哈哈哈哈,不過grunt好像後知後覺,不知道如今怎麼樣了,很久沒有關注grunt了。

Grunt處理任務的流程:
gruntjquery

整體來講,我我的以爲gulp是比grunt更爲優雅的一種構建工具~,哈哈。webpack

此外,gulpgrunt都須要一個構建文件來支持構建,grunt的是gruntfile.jsgulp的是gulpfile.js
經過兩個文件來對比一下:
gruntfile.js文件,我以前項目中的大概是這麼個樣子:git

'use strict';

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    cssmin: {
      minify: {
        expand: true,
        cwd: 'css/',
        src: ['*.css', '!*.min.css'],
        dest: 'css/',
        ext: '.min.css'
      },
      my_target: {
        files: [{
          expand: true,
          cwd: '../style/',
          src: ['*.css', '!*.min.css'],
          dest: '../build/style/'
        }]
      }
    },
    copy: {
      main: {
        files: [
          {
            expand: true,
            cwd: '../views/',
            src: ['**'],
            dest: '../build/views/'
          }, {
            expand: true,
            cwd: '../img/',
            src: ['**'],
            dest: '../build/img/'
          }, {
            expand: true,
            cwd: '../',
            src: ['main.html'],
            dest: '../build/'
          }, {
            expand: true,
            cwd: '../',
            src: ['*.txt'],
            dest: '../build/'
          }
        ]
      }
    },
    uglify: {
      buildall: {
        files: [{
          expand: true, 
          cwd: '../config/', 
          src: '**/*.js', 
          dest: '../build/config/' 
        }, {
          expand: true, 
          cwd: '../js/controllers/', 
          src: '**/*.js', 
          dest: '../build/js/controllers/' 
        }, {
          expand: true,
          cwd: '../js/directive/',
          src: '**/*.js', 
          dest: '../build/js/directive/' 
        }, {
          expand: true, 
          cwd: '../js/filter/', 
          src: '**/*.js',
          dest: '../build/js/filter/' 
        }, {
          expand: true,
          cwd: '../js/service/', 
          src: '**/*.js', 
          dest: '../build/js/service/' 
        }]
      },
      builda: {
        files: {
          '../build/js/app.js': ['../js/app.js'],
          '../build/js/libs/bridge.1.1.0.js': ['../js/libs/bridge.1.1.0.js']
        }
      },
      release: {
        files: {
          '../build/js/libs/libs.min.js': ['../js/libs/zepto.20140520.js', '../js/libs/angular.min.js', '../js/libs/*.js', '!../js/libs/bridge.1.1.0.js', '!../js/libs/libs.min.js']
        }
      }
    }
  });
  grunt.loadNpmTasks('grunt-contrib-copy');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-uglify'); 
  grunt.loadNpmTasks('grunt-contrib-cssmin'); 
  
  grunt.registerTask('default', ['copy', 'cssmin', 'uglify']);

};

再來看gulp的:

var gulp = require('gulp'),//gulp基礎庫
    concat = require('gulp-concat'),//合併文件
    cssmin = require('gulp-minify-css'),//壓縮css
    htmlmin = require("gulp-htmlmin"),//壓縮html
    jsmin = require('gulp-uglify'),//壓縮js
    rename = require('gulp-rename'),//重命名文件
    clean = require("gulp-clean"),//清理目錄
    replace = require('gulp-replace'),//文本替換
    processhtml = require('gulp-processhtml'),//處理html文件
    addsrc = require('gulp-add-src'),//添加額外的文件流
    option = {
        buildPath: "../dist"//構建目錄
    };
//構建目錄清理
gulp.task("clean", function (done) {
    //return cache.clearAll(done);
    return gulp.src(option.buildPath, {
        read: false
    })
    .pipe(clean({force: true}));

})

gulp.task("imgcopy", function () {//圖片拷貝
    gulp.src("../img/**/*")
    .pipe(gulp.dest(option.buildPath + '/img/'))
})

//js文件壓縮
gulp.task('jsmin', function () {
    gulp.src(["../js/**/**/*.js",'!../js/libs/*.js'])
        .pipe(jsmin())
        .pipe(gulp.dest(option.buildPath+ "/js/"))
});

//須要合併和壓縮的文件
gulp.task('concat', function () {
    gulp.src(['../js/libs/angular.min.js','../js/libs/*.js', '!../js/libs/bridge*.js'])
        .pipe(concat('libs.min.js'))
        .pipe(jsmin())
        .pipe(addsrc('../js/libs/bridge*.js'))
        .pipe(jsmin())
        .pipe(gulp.dest(option.buildPath + "/js/libs/"))
});

gulp.task("processhtml", function () {
    var date = new Date().getTime();
    gulp.src('../main.html')
        .pipe(replace(/_VERSION_/gi, date))
        .pipe(processhtml())
        .pipe(htmlmin({
            collapseWhitespace: true
        }))
        .pipe(gulp.dest(option.buildPath + '/'))
})

//壓縮css
gulp.task("cssmin", function () {
    gulp.src("../style/*.css")
        .pipe(cssmin())
        .pipe(gulp.dest(option.buildPath + '/style'))
})

//壓縮html文件
gulp.task("htmlmin", function () {
    gulp.src('../views/**/*.html')
        .pipe(htmlmin({
            collapseWhitespace: true
        }))
        .pipe(gulp.dest(option.buildPath + '/views'))
})

// 默認任務 清空圖片、樣式、js並重建 運行語句 gulp
gulp.task('default', ['clean'], function () {
    gulp.start('jsmin', 'cssmin', 'processhtml', "htmlmin", 'imgcopy', 'concat');
});
注:上面兩個文件只是拿來作對比說明 gulpgrunt的書寫方式不一樣,具體的任務二者並不徹底匹配。

gulp爲何快

  1. 使用orchestrator任務系統,最大限度的併發運行多個任務.
  2. 每一個插件只作一件事,作好一件事, 提高處理速度
  3. 流式處理,極少的文件IO

gulp快速上手

如下是一分鐘上手教程:

  1. 首先確保你已經正確安裝了nodejs環境。而後以全局方式安裝gulp

    npm install -g gulp
  2. 全局安裝gulp後,還須要在每一個要使用gulp的項目中都單獨安裝一次。把目錄切換到你的項目文件夾中,而後在命令行中執行:

    npm install gulp

    若是想在安裝的時候把gulp寫進項目package.json文件的依賴中,則能夠加上–D

    npm install -D gulp
  3. 在項目根目錄建立gulpfile.js文件

    var gulp = require('gulp');
    gulp.task('default', function() {
        console.log('hello world');
    });
  4. 運行gulp

    gulp

    默認任務將被運行,向控制檯輸出hello world
    若是須要運行單個任務, 使用 gulp taskname命令。

gulp api速覽

使用gulp,僅需知道4個API便可:

  • gulp.task()
  • gulp.src()
  • gulp.dest()
  • gulp.watch()

因此很容易就能掌握。

gulp.src(globs[, options])

gulp處理文件的流程是【讀取文件】、【用插件處理文件】、【輸出文件】,gulp.src()正式用來讀取要操做的文件的,globs參數是文件匹配模式(相似正則表達式),用來匹配文件路徑(包括文件名),固然這裏也能夠直接指定某個具體的文件路徑。當有多個匹配模式時,該參數能夠爲一個數組。

globs 文件匹配模式

gulp內部使用了node-glob模塊來實現其文件匹配功能。舉些栗子:

  • * 匹配文件路徑中的0個或多個字符,但不會匹配路徑分隔符,除非路徑分隔符出如今末尾
  • ** 匹配路徑中的0個或多個目錄及其子目錄,須要單獨出現,即它左右不能有其餘東西了。若是出如今末尾,也能匹配文件。
  • ?匹配文件路徑中的一個字符(不會匹配路徑分隔符)
  • [...] 匹配方括號中出現的字符中的任意一個,當方括號中第一個字符爲^或!時,則表示不匹配方括號中出現的其餘字符中的任意一個,相似js正則表達式中的用法!(pattern|pattern|pattern)匹配任何與括號中給定的任一模式都不匹配的
  • ?(pattern|pattern|pattern)匹配括號中給定的任一模式0次或1次,相似於js正則中的(pattern|pattern|pattern)?
  • +(pattern|pattern|pattern)匹配括號中給定的任一模式至少1次,相似於js正則中的(pattern|pattern|pattern)+
  • *(pattern|pattern|pattern)匹配括號中給定的任一模式0次或屢次,相似於js正則中的(pattern|pattern|pattern)*
  • @(pattern|pattern|pattern)匹配括號中給定的任一模式1次,相似於js正則中的(pattern|pattern|pattern)

文件匹配列子:

  • * 能匹配 reeoo.js,reeoo.css,reeoo,reeoo/,但不能匹配reeoo/reeoo.js
  • *.*能匹配 reeoo.js,reeoo.css,reeoo.html
  • */*/*.js能匹配 r/e/o.js,a/b/c.js,不能匹配a/b.js,a/b/c/d.js
  • **能匹配 reeoo,reeoo/reeoo.js,reeoo/reeoo/reeoo.js,reeoo/reeoo/reeoo,reeoo/reeoo/reeoo/reeoo.co,能用來匹配全部的目錄和文件
  • **/*.js能匹配 reeoo.js,reeoo/reeoo.js,reeoo/reeoo/reeoo.js,reeoo/reeoo/reeoo/reeoo.js
  • reeoo/**/co能匹配 reeoo/co,reeoo/ooo/co,reeoo/a/b/co,reeoo/d/g/h/j/k/co
  • reeoo/**b/co能匹配 reeoo/b/co,reeoo/nb/co,但不能匹配reeoo/x/nb/co,由於只有單**單獨出現才能匹配多級目錄
  • ?.js能匹配 reeoo.js,reeoo1.js,reeoo2.js
  • reeoo??能匹配 reeoo.co,reeooco,但不能匹配reeooco/,由於它不會匹配路徑分隔符
  • [reo].js只能匹配 r.js,e.js,o.js,不會匹配re.js,reo.js等,整個中括號只表明一個字符
  • [^reo].js能匹配 a.js,b.js,c.js等,不能匹配r.js,e.js,o.js

當有多種匹配模式時可使用數組

//使用數組的方式來匹配多種文件
gulp.src(['js/*.js','css/*.css','*.html'])

使用數組的方式還有一個好處就是能夠很方便的使用排除模式,在數組中的單個匹配模式前加上!便是排除模式,它會在匹配的結果中排除這個匹配,要注意一點的是【不能在數組中的第一個元素】中使用排除模式,用了也無效!

gulp.src([*.js,'!r*.js']) 匹配全部js文件,但排除掉r開頭的js文件
gulp.src(['!r*.js',*.js]) 不會排除任何文件,由於排除模式不能出如今數組的第一個元素中
此外,還可使用展開模式。展開模式以花括號做爲定界符,根據它裏面的內容,會展開爲多個模式,最後匹配的結果爲全部展開的模式相加起來獲得的結果。展開的例子以下:

  • r{e,o}c會展開爲 recroc
  • r{e,}o會展開爲 reoro
  • r{0..3}o會展開爲 r0or1dor2or3o

options

options主要用來配置一些跟文件流相關的額外的信息,主要有三個參數:

options.buffer

默認值是true,若是設置爲false,則會覺得stream的方式返回文件內容(file.contents),而不是buffer方式,若是對buffer或者stream不瞭解,能夠去nodejs官網看下文檔。

options.read

默認值是true
若是設置爲false,那麼file.contents會返回空值,也就是並不會去讀取文件,這個值如無需求,最好不要改~

options.base

base的值很神奇,它能夠影響到你插件最終輸出的文件的路徑,默認狀況下,gulp會根據gulp.src匹配到的路徑自動設置base的值,若是你發現這個base知足不了你的需求,就能夠手動指定base
舉個栗子:

// 匹配40017/js/vendor/vue.js,並將base解析成 40017/js
gulp.src('40017/js/**/*.js')
  .pipe(uglify())//壓縮
  // 處理完畢的文件,輸出的時候,會用build替換掉base,即輸出文件的路徑爲 build/vendor/vue.js
  .pipe(gulp.dest('build'));

// 手動設置base的值
gulp.src('40017/js/**/*.js', { base: '40017' })
  .pipe(uglify())//壓縮
  // 處理完畢的文件,輸出的時候,會用build替換掉base,即輸出文件的路徑爲 build/js/vendor/vue.js
  .pipe(gulp.dest('build'));

gulp.dest(path[,options])

gulp.src呼應的是gulp.dest(),這個方法是用來寫文件的,path爲寫入文件的路徑,options爲一個可選的參數對象,用了這麼長時間的gulp,這個參數還真是一次都沒用過,今天就不扯了。
要想使用好gulp.dest()這個方法,就要理解給它傳入的路徑參數與最終生成的文件的關係。
gulp的使用流程通常是這樣子的:首先經過gulp.src()方法獲取到咱們想要處理的文件流,而後把文件流經過pipe方法導入到gulp的插件中,最後把通過插件處理後的流再經過pipe方法導入到gulp.dest()中,gulp.dest()方法則把流中的內容寫入到文件中,這裏首先須要弄清楚的一點是,咱們給gulp.dest()傳入的路徑參數,只能用來指定要生成的文件的目錄,而不能指定生成文件的文件名,它生成文件的文件名使用的是導入到它的文件流自身的文件名,因此生成的文件名是由導入到它的文件流決定的,即便咱們給它傳入一個帶有文件名的路徑參數,而後它也會把這個文件名當作是目錄名,例如:

var gulp = require('gulp');
gulp.src('40017/js/vue/vendor.js')
    .pipe(gulp.dest('dist/vue.js'));
//最終生成的文件路徑爲 dist/vue.js/vendor.js,而不是dist/vue.js

要想改變文件名,可使用插件gulp-rename

下面說說生成的文件路徑與咱們給gulp.dest()方法傳入的路徑參數之間的關係。
gulp.dest(path)生成的文件路徑是咱們傳入的path參數後面再加上gulp.src()中有通配符開始出現的那部分路徑。例如:

var gulp = reruire('gulp');
//有通配符開始出現的那部分路徑爲 **/*.js
gulp.src('js/**/*.js')
    .pipe(gulp.dest('dist')); //最後生成的文件路徑爲 dist/**/*.js
//若是 **/*.js 匹配到的文件爲 jquery/jquery.js ,則生成的文件路徑爲 dist/jquery/jquery.js

Two more 栗子

gulp.src('js/**/fish.js')
    //假設匹配到的文件爲js/lib/fish.js
    .pipe(gulp.dest('dist')); //則最後生成的文件路徑爲 dist/lib/fish.js

gulp.src('js/*') //有通配符出現的那部分路徑爲 *
    //假設匹配到的文件爲js/4in1.js
    .pipe(gulp.dest('dist')); //則最後生成的文件路徑爲 dist/4in1.js
gulp.src('js/vue/vendor.js') //沒有通配符出現的狀況
    .pipe(gulp.dest('dist')); //最後生成的文件路徑爲 dist/vendor.js

經過指定gulp.src()方法配置參數中的base屬性,咱們能夠更靈活的來改變gulp.dest()生成的文件路徑(前面已經說過)。
當咱們沒有在gulp.src()方法中配置base屬性時,base的默認值爲通配符開始出現以前那部分路徑,例如:

gulp.src('src/js/**/*.js') //此時base的值爲 src/js

上面咱們說的gulp.dest()所生成的文件路徑的規則,其實也能夠理解成,用咱們給gulp.dest()傳入的路徑替換掉gulp.src()中的base路徑,最終獲得生成文件的路徑。

gulp.src('src/js/**/*.js') //此時base的值爲src/js,也就是說它的base路徑爲src/js
     //設該模式匹配到了文件 src/js/common/common.js
    .pipe(gulp.dest('dist')) //用dist替換掉base路徑,最終獲得 dist/common/common.js

因此改變base路徑後,gulp.dest()生成的文件路徑也會改變,這個前面說過了,這裏就不贅述了。

gulp.dest()把文件流寫入文件後,若是還有後續操做的話,文件流仍然能夠繼續使用。

gulp.task(name[, deps], fn)

gulp.task方法用來定義任務,
name 爲任務名,
deps 是當前定義的任務須要依賴的其餘任務列表,
爲一個數組。當前定義的任務會在全部依賴的任務執行完畢後纔開始執行。
若是沒有依賴,則可省略這個參數,
fn 爲具體的任務函數,咱們把任務要執行的代碼都寫在裏面。該參數也是可選的。

栗子(HTML壓縮):

let gulp = require("gulp");
let htmlmin = require("gulp-htmlmin");
gulp.task("htmlmin",()=>{
    gulp.src("src/**/*.html")
        .pipe(htmlmin({
            collapseWhitespace: true,
            removeComments: true,               //清除HTML註釋
            collapseBooleanAttributes: true,    //省略布爾屬性的值 <input checked="true"/> ==> <input />
            removeScriptTypeAttributes: true,       //刪除<script>的type="text/javascript"
            removeStyleLinkTypeAttributes: true,    //刪除<style>和<link>的type="text/css"
            minifyJS: true,                         //壓縮頁面JS
            minifyCSS: true                     //壓縮頁面CSS
        })))
        .pipe(gulp.dest("dist"))
})

gulp.watch(glob[, opts], tasks)

gulp.watch()用來監視文件的變化,當文件發生變化後,咱們能夠利用它來執行相應的任務,例如文件壓縮等,這樣就不須要在文件改變的時候,手動去執行任務了。
glob 爲要監視的文件匹配模式,規則和用法與gulp.src()方法中的glob相同,再也不贅述。
opts 爲一個可選的配置對象,一般不須要用到,略過。
tasks 爲文件變化後要執行的任務,爲一個數組,一個或者多個經過 gulp.task() 建立的 task的名字的列表。

gulp.task('uglify',function(){
  //do something
});
gulp.task('reload',function(){
  //do something
});
gulp.watch('js/**/*.js', ['uglify','reload']);//在js文件發生變化的時候執行uglify和reload兩個任務

gulp.watch()還有另一種使用形式:

gulp.watch(glob[, opts, cb])

globopts參數略過,這裏跟上面的那種方式惟一不一樣的地方就是第三個參數了,cb參數爲一個函數。每當監視的文件發生變化時,就會調用這個函數,而且會給它傳入一個對象,該對象包含了文件變化的一些信息,type屬性爲變化的類型,能夠是added,changed,deletedpath屬性爲發生變化的文件的路徑

gulp.watch('js/**/*.js', function(event){
    console.log(event.type); //變化類型 added爲新增,deleted爲刪除,changed爲改變 
    console.log(event.path); //變化的文件的路徑
});

OK,gulpApi就介紹這麼多了,是否是很簡單?

經常使用的gulp插件介紹

js文件壓縮

使用gulp-uglify
安裝:npm install --save-dev gulp-uglify
用來壓縮js文件,使用的是UglifyJs引擎

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

gulp.task('minify-js', function () {
    gulp.src('js/*.js') // 要壓縮的js文件
    .pipe(uglify())
    .pipe(gulp.dest('dist/js')); //壓縮後的路徑
});

重命名文件

使用gulp-rename
安裝:npm install --save-dev gulp-rename
用來重命名文件流中的文件。用gulp.dest()方法寫入文件時,文件名使用的是文件流中的文件名,若是要想改變文件名,那能夠在以前用gulp-rename插件來改變文件流中的文件名。

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

gulp.task('rename', function () {
    gulp.src('js/vue.js')
    .pipe(uglify())  //壓縮
    .pipe(rename('vue.min.js')) //vue.js重命名爲vue.min.js
    .pipe(gulp.dest('js'));
});

壓縮css文件

以前的壓縮插件gulp-minify-css已經廢棄
如今推薦使用的是gulp-clean-css
安裝:npm install gulp-clean-css --save-dev
栗子:

var gulp = require('gulp'),
    cleanCSS = require("gulp-clean-css");

gulp.task('minify-css', function () {
    gulp.src('css/*.css') // 要壓縮的css文件
    .pipe(cleanCSS()) //壓縮css
    .pipe(gulp.dest('dist/css'));
});

html文件壓縮

使用gulp-minify-html
安裝:npm install --save-dev gulp-minify-html

var gulp = require('gulp'),
    minifyHtml = require("gulp-minify-html");

gulp.task('minify-html', function () {
    gulp.src('html/*.html') // 要壓縮的html文件
    .pipe(minifyHtml()) //壓縮
    .pipe(gulp.dest('dist/html'));
});

文件合併

使用gulp-concat
安裝:npm install --save-dev gulp-concat
用來把多個文件合併爲一個文件,咱們能夠用它來合併jscss文件等,這樣就能減小頁面的http請求數。

var gulp = require('gulp'),
    concat = require("gulp-concat");

gulp.task('concat', function () {
    gulp.src('js/*.js')  //要合併的文件
    .pipe(concat('all.js'))  // 合併匹配到的js文件並命名爲 "all.js"
    .pipe(gulp.dest('dist/js'));
});

處理html

使用gulp-processhtml
安裝:npm install --save-dev gulp-processhtml
在構建時處理按你設想的修改html文件,好比說你構建以前你有N個腳本須要引入到html頁面中,構建以後可能這N個文件被合併成了一個,這時候引入的地方也須要作相應的調整,那麼差個插件就能派上用場了。
插件使用

gulp.task("processhtml", function () {
    gulp.src('../main.html')
        .pipe(processhtml())
        .pipe(gulp.dest("dist"))
})

main.html構建以前的代碼

<!DOCTYPE html>
<html ng-app="app">
<head lang="en">
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"/>
    <meta name="format-detection" content="telephone=no"/>
    <link rel="stylesheet" href="style/base.css?/>
    <link rel="stylesheet" href="style/index.css?/>
</head>
<body>
<ui-view></ui-view>
</body>
<!-- build:js js/libs/libs.min.js --> <!--process-html插件須要用到這個註釋-- >
<script src="js/libs/angular.min.js"></script>
<script src="js/libs/angular.touch.min.js"></script>
<script src="js/libs/zepto.20140520.js"></script>
<script src="js/libs/angular.ui.bootstrap.js"></script>
<script src="js/libs/angular-sanitize.min.js"></script>
<script src="js/libs/angular-ui-route.min.js"></script>
<script src="js/libs/fastclick.0.6.7.min.js"></script>
<script src="js/libs/carous.min.js"></script>
<!-- /build --> <!--process-html插件須要用到這個註釋-->
</html>

main.html構建以後

<!DOCTYPE html>
<html ng-app="app">
<head lang="en">
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"/>
    <meta name="format-detection" content="telephone=no"/>
    <link rel="stylesheet" href="style/base.css?/>
    <link rel="stylesheet" href="style/index.css?/>
</head>
<body>
<ui-view></ui-view>
</body>
<script src="js/libs/libs.min.js"></script> <!--注意這裏的變化-->
</html>

字符串替換

使用gulp-replace
安裝:npm install --save-dev gulp-replace
能夠替換html或者txt等文件內的字符串爲你想要的。
好比我每次構建的時候都須要去修改引用的文件的版本號,就可使用這個插件

gulp.task("replace", function () {
    var date = new Date().getTime();
    gulp.src('../main.html')
        .pipe(replace(/_VERSION_/gi, date))
        .pipe(gulp.dest("dist"))
})

main.html文件

<!DOCTYPE html>
<html ng-app="app">
<head lang="en">
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"/>
    <meta name="format-detection" content="telephone=no"/>
    <link rel="stylesheet" href="style/base.css?v=_VERSION_"/>
    <link rel="stylesheet" href="style/index.css?v=_VERSION_"/>
</head>
<body>
<ui-view></ui-view>
</body>
<script src="js/config/config.js?v=_VERSION_"></script>
<script src="js/app.js?v=_VERSION_"></script>
<script src="js/service/TrackDataService.js?v=_VERSION_"></script>
<script src="js/service/APIService.js?v=_VERSION_"></script>
<script src="js/service/DService.js?v=_VERSION_"></script>
<script src="js/controllers/indexCtrl.js?v=_VERSION_"></script>
<script src="js/directive/lazy.js?v=_VERSION_"></script>
<script src="js/directive/slider.js?v=_VERSION_"></script>
<script src="js/filter/filters.js?v=_VERSION_"></script>
</html>

構建以後,_VERSION_被替換爲時間戳。

<!DOCTYPE html>
<html ng-app="app">
<head lang="en">
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"/>
    <meta name="format-detection" content="telephone=no"/>
    <link rel="stylesheet" href="style/base.css?v=1433405631860"/>
    <link rel="stylesheet" href="style/index.css?v=1433405631860"/>
</head>
<body>
<ui-view></ui-view>
</body>
<script src="js/config/config.js?v=1433405631860"></script>
<script src="js/app.js?v=1433405631860"></script>
<script src="js/service/TrackDataService.js?v=1433405631860"></script>
<script src="js/service/APIService.js?v=1433405631860"></script>
<script src="js/service/DService.js?v=1433405631860"></script>
<script src="js/controllers/indexCtrl.js?v=1433405631860"></script>
<script src="js/directive/lazy.js?v=1433405631860"></script>
<script src="js/directive/slider.js?v=1433405631860"></script>
<script src="js/filter/filters.js?v=1433405631860"></script>
</html>

gulp-bytediff

gulp-bytediff這個插件,你能夠看到gulp處理先後文件的大小變化。
栗子:

let uglify = require("gulp-uglify");
let util = require("gulp-util");
let bytediff = require("gulp-bytediff");
function bytediffFormatter(data){
    let difference = (data.savings > 0) ? ' smaller.' : ' larger.',
            result = data.savings > 0 ? ('and is ' +
            util.colors.yellow.bold(~~((1 - data.percent) * 100) + '%') + difference) : "";
        return `${util.colors.cyan.bold(data.fileName)} from ${util.colors.yellow.bold((data.startSize / 1000).toFixed(2))} kB to ${util.colors.yellow.bold((data.endSize / 1000).toFixed(2))} kB ${result}`;
}

gulp.task("bd",()=>{
    gulp.src("src/js/**/*.js")
    .pipe(bytediff.start())
    .pipe(uglify())
    .pipe(bytediff.stop(bytediffFormatter))
    .pipe(gulp.dest("dist")) 
})

console會輸出以下信息:

bytediff

gulp-sequence

因爲gulp執行的多個任務的時候,任務之間彼此是異步的,不能確保執行順訊,這個插件可讓一系列的任務按順序執行。

安裝

npm install --save-dev gulp-sequence

使用:

var gulp = require('gulp')
var gulpSequence = require('gulp-sequence')
 
gulp.task('a', function (cb) {
  //doSomething
})
 
gulp.task('b', function (cb) {
  //doSomething
})
 
gulp.task('c', function (cb) {
  //doSomething
})

gulp.task('d', function () {
  // return stream
  return gulp.src('js/**/*.js')
})

// 1. 並行執行a、b;
// 2. 執行完a、b以後執行c;
// 3. 執行完c以後,執行d;
gulp.task('default', gulpSequence(['a', 'b'], 'c', 'd'))

自動刷新

能夠結合browser-syncgulp.watch()作多終端自動刷新,只要文件發生變化,就能夠自動構建,而後瀏覽器自動刷新代碼,相似於webpack的熱替換。
安裝:
npm i browser-sync -D
栗子:

const browserSync = require("browser-sync");
const uglify = require("gulp-uglify");
const sequence = require("gulp-sequence");//按順序執行
gulp.task("uglify",()=>{
    return gulp.src('js/**/*js')
        .pipe(uglify())
        .pipe(gulp.dest('dist'));
})

gulp.task('watch', () => {
    const watcher = gulp.watch("js/**/*.js", ["uglify"]);
    wathcer.on('change', function (path) {
        browserSync.reload();
    })
})

gulp.task('server', () => {
    //靜態服務器
    browserSync({
        server: {
            baseDir: `dist` //從這個項目的dist目錄啓動服務器
        },
        open: "external",//Browsersync啓動時自動打開外部網址,也就是咱們的本地IP加端口
        logConnections: true,
        // directory: true,//是否列出目錄
        startPath: "/main.html",//服務器啓動以後默認訪問哪一個頁面
    });
})

gulp.task("default",sequence(['uglify'], ['server'], ['watch']))

模塊化管理插件

上面說到的插件都是用commonjs的方式手動引入的,好比 let bs = require("browser-sync"),使用gulp-load-plugins這個插件,能夠自動地從依賴文件中提取插件並注入到你想要的對象中,無需手動引入。

插件安裝:

npm install --save-dev gulp-load-plugins

栗子:

let $ = require('gulp-load-plugins')({//初始化各類插件引用
    pattern: ['gulp-*', 'imagemin-pngquant', 'browser-sync'],//搜索插件的正則
    replaceString: /\bgulp[\-.]/,//當把插件加載到執行環境的時候,去除哪些文本
    lazy: true,//是否懶加載插件,true,只有用到的時候纔去加載,false反之
    camelize: true//設置爲true則插件被加載進來的時候名稱是駝峯形式的
})

好比以前安裝了gulp-clean-css的插件,那麼如今這須要這麼用:

var gulp = require('gulp'),
    //cleanCSS = require("gulp-clean-css");這個就不用了

//gulp-load-plugins會根據搜索的正則去找gulp-clean-css這個插件,並把它名字前面的gulp去掉,而後把剩下的轉成駝峯`cleanCss`,這就是待會要使用的插件的方法名字了,這個方法是掛在前面已經定義好的對象$上的,因此能夠直接$.cleanCss()這樣去調用。

gulp.task('minify-css', function () {
    gulp.src('css/*.css') // 要壓縮的css文件
    .pipe($.cleanCSS()) //壓縮css
    .pipe(gulp.dest('dist/css'));
});

其餘的插件使用方式大同小異。
上面只介紹了gulp-load-plugins一些經常使用的參數,其餘的參數你們能夠去npm查看。

gulp還有不少插件,你們能夠去gulp官網查看

寫在最後

有人會說,webpack都一統天下了,你怎麼還在折騰gulp
大多數人都以爲webpack是目前前端工程化完整性解決方案,無論你什麼grunt仍是gulp都得給我靠邊站,只要使用webpack就足夠了。

真的是這樣嗎???

webpack官網給自身的定位是webpack is a module bundler,也就是說,webpack是模塊打包器,webpack的競爭對手應該是browserify之流,而不是gulp或是grunt這類Task Runner

我記得以前webpack官方也給出了gulp中使用webpack的方式(webpack1.x的文檔已經被刪了,找不到了,呵呵)

雖然webpack能夠替代gulp的一些功能,可是我不以爲這二者是非此即彼的狀態,工具只選合適的。

來,乾了這杯大可樂~

如對 gulp還有什麼不明白之處,或者本文有什麼遺漏或錯誤,歡迎一塊兒交流。
相關文章
相關標籤/搜索