gulp進階構建項目由淺入深

gulp進階構建項目由淺入深css

閱讀目錄html

gulp基本安裝和使用

    Gulp的構建過程:gulp是使用nodejs中的stream(流),首先經過gulp.src()方法獲取到咱們須要的文件流(stream),而後把文件流經過pipe()方法把流導入到gulp的插件中,最後經過插件處理後的流再經過pipe()方法導入到gulp.dest()中,gulp.dest()方法把流中的內容寫入到文件中。

1. Gulp安裝:

首先咱們須要安裝nodejs,而後進行全局安裝;安裝以下:

   sudo npm install gulp –g

全局安裝後,還須要切換到項目的根目錄下,單獨爲單個項目進行安裝下;安裝以下:

   sudo npm install gulp 

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

   sudo npm install –save-dev  gulp

2. 如何使用gulp?

在項目的根目錄下新建一個 gulpfile.js文件,以後就能夠定義一個任務了;

好比以下簡單的任務:代碼以下:

var gulp = require('gulp');

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

    console.log('hello world');

});

如今我項目的目錄結構假如是以下樣子:

最後咱們進行命令行切換到項目的根目錄下,運行gulp命令後,就能夠在控制檯看到consoe.log的打印的消息了;  

gulp API介紹

Gulp.src(globs[,options])

最多見的咱們使用四個API,gulp.task() gulp.src() gulp.dest() gulp.watch();

該方法是獲取咱們須要的文件流,這個流裏面的內容不是原始的文件流,而是一個虛擬文件對象流(Vinyl files);該方法有2個參數

globs類型是 String 或 Array , 該文件流能夠是一個單獨的字符串形式,也能夠是一個數組形式;

options是一個對象類型;該對象類型有一個咱們經常使用的base字段配置 options.base是常常會使用的到;

好比以下代碼:

var gulp = require('gulp');
var uglify = require('gulp-uglify');
gulp.task("uglify-js",function(){
return gulp.src("src/js/*.js")
     .pipe(uglify())
     .pipe(gulp.dest('build'));
});
gulp.task('default',['uglify-js']);
// 寫入到 build/a.js 和 build/index.js

以下目錄結構:

咱們使用base字段來繼續編寫以下代碼:

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

gulp.task("uglify-js",function(){
    return gulp.src("src/js/*.js",{ base: 'src' })
        .pipe(uglify())
        .pipe(gulp.dest('build'));
});
gulp.task('default',['uglify-js']);

咱們再在項目的根目錄下面運行gulp命令能夠看到項目的目錄結構變爲以下:

所以咱們能夠理解base字段爲相對於路徑來進行打包,最後生成 build/js/*.js文件;

看看Gulp用到的glob的匹配規則:

Gulp內部使用了node-glob模塊來實現文件匹配功能。該文件匹配相似於JS中的正則表達式;以下匹配:

         匹配文件路徑中的0個或者多個字符,可是不會匹配路徑分隔符。好比: 能夠匹配 abc.js,x.js,aaa,abc/(路徑分隔符出如今末尾也能夠匹配);可是不能匹配相似於這樣的路徑分隔符 abc/aa.js

        *.* 能夠匹配a.xxx; xxxx.yyyy;等

        */*/*.js  能夠匹配a/b/c.js,但不是不能匹配 a/b.js 或者 a/b/c/d.js

        **  能夠匹配路徑中的0個或者多個目錄及其子目錄。好比:能匹配abc,a/b.js,

a/b/c.js,x/y/z等等;

        **/*.js  可以匹配a.js , a/a.js,a/aaa/aaaa/a.js等等;也就是說只要以.js結尾的,無論前面有多少個文件或者分隔符均可以匹配;

        a/**/z  能匹配a/z,a/b/z, a/b/c/z等等。

 a/**b/z  能匹配a/b/z,a/sb/z, 不是不能匹配a/x/y/xxb/z;          

?.js  能匹配a.js,b.js,c.js,至關於js正則裏面的同樣匹配0個或者1個,優先匹配;

[xyz].js 能匹配x.js,y.js,z.js,相似於js正則同樣,中括號中的任意一個字符;

    [^xyz].js 除了中括號中的x,y,z中的其餘的任意一個字符;

當有多種匹配模式的時候可使用數組,以下:

gulp.src([‘js/*.js’,’css/*.css’]);

咱們也能夠排除一些文件,可使用!, 好比以下代碼:

gulp.src([‘js/*.js’,’css/*.css’,’!reset.css’]) ; 匹配全部的js/目錄下的js文件及匹配css/目錄下的css文件,可是不包括reset.css文件;可是不能把排除寫在第一個元素位置;

好比以下代碼: gulp.src([‘!reset.css’,’css/*.css’]); 這樣的是排除不掉的,這種方式咱們在css中能夠理解爲後面的代碼覆蓋前面的,所以須要寫在後面才能排除當前的;

gulp.dest(path[,options])

   該方法能夠理解爲把目標的源文件經過pipe方法導入到gulp插件中,最後把文件流寫到目標文件中;若是該文件不存在的話,則會自動建立它;好比上面的gulp.src(),目錄結構一剛開始build目錄是沒有的,經過打包後自動建立build文件夾;

字段path: 文件被寫入的路徑(輸出的目錄),也能夠傳入一個函數,在函數中返回相應的路徑。

字段options也是一個對象類型;

    Gulp.dest(path) 生成的文件路徑是相對於gulp.src()中有通配符開始出現的那部分路徑。

        好比以下代碼:

var gulp = require('gulp');
var uglify = require('gulp-uglify');
gulp.task("uglify-js",function(){
    return gulp.src("src/js/*.js")
        .pipe(uglify())
        .pipe(gulp.dest('build'));
});
gulp.task('default',['uglify-js']);

gulp.src()上面有通配符的是 *.js, 所以最後生成的文件路徑是 build/*.js;
可是若是沒有出現通配符的狀況下,好比以下代碼:
gulp.src("src/js/a.js")
.pipe(gulp.dest('build'));

那麼最後生成的路徑就是 build/a.js 了;

固然咱們能夠在gulp.src()方法中配置base屬性,若是沒有配置base屬性的話,那麼默認生成的路徑就是相對於通配符出現的那部分路徑;若是設置了base屬性的話,那麼就相對於base的那個設置的路徑;假如如今的源目錄結構爲src/js/下游不少js文件

好比以下代碼:

var gulp = require('gulp');
var uglify = require('gulp-uglify');
gulp.task("uglify-js",function(){
    return gulp.src("src/js/*.js",{ base: 'src' })
        .pipe(uglify())
        .pipe(gulp.dest('build'));
});
gulp.task('default',['uglify-js']);

是相對於src文件下的,所以最後生成的路徑爲 build/js/*.js 

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

該方法是定義一個任務;

name: 是任務的名字;

deps: {Array} 類型是數組類型;一個包含任務列表的數組,這些任務會在你當前任務運行以前完成;好比以下代碼:

gulp.task('mytask', ['one', 'two', 'task', 'names'], function() {

    // 作一些事

});

好比上面的代碼,咱們想要完成'mytask'這個任務的話,首先會執行依賴數組中的那些任務,可是若是依賴任務裏面又使用了異步的方法,好比使用setTimeout這樣的時候,那麼這個時候,我再執行mytask這個任務的時候,就不會等待該依賴任務完成後再執行了;好比以下代碼:

var gulp = require('gulp');
gulp.task('one',function(){
  //one是一個異步執行的任務
  setTimeout(function(){
    console.log('one is done')
  },5000);
});
//two任務雖然依賴於one任務,但並不會等到one任務中的異步操做完成後再執行
gulp.task('two',['one'], function(){
  console.log('two is done');
});
gulp.task('default',['two']);

上面的例子中咱們執行two任務時,會先執行one任務,但不會去等待one任務中的異步操做完成後再執行two任務,而是緊接着執行two任務。因此two任務會在one任務中的異步操做完成以前就執行了。

可是若是咱們想等待one任務中的setTimeout執行完成後,再執行two這個任務該怎麼辦呢?

咱們可使用以下方法,代碼以下:

var gulp = require('gulp');
gulp.task('one',function(fn){
  // fn 爲任務函數提供的回調 用來通知該任務已經完成
  //one是一個異步執行的任務
  setTimeout(function(){
    console.log('one is done');
    fn();
  },1000);
});
//two任務雖然依賴於one任務,但並不會等到one任務中的異步操做完成後再執行
gulp.task('two',['one'], function(){
  console.log('two is done');
});
gulp.task('default',['two']);

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

用來監聽文件的變化,當文件發生改變的時候,咱們能夠用它來執行相應的任務;

參數以下:

glob: 爲要監聽的文件匹配模式,規則和gulp.src中的glob相同;

opts: 爲一個可選的配置對象,通常不怎麼用;

tasks: 爲文件變化後要執行的任務,爲一個數組;

好比代碼以下:

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

  console.log('two is done');

});

gulp.watch('js/**/*.js',['two'])

gulp一些經常使用插件

1.gulp-rename(重命名)

用來重命名文件流中的文件。

安裝:npm install --save-dev gulp-rename

好比以下代碼:

var gulp = require('gulp'),
    rename = require('gulp-rename'),
    uglify = require("gulp-uglify");
 
gulp.task('rename', function () {
    gulp.src('src/**/*.js')
    .pipe(uglify())  //壓縮
    .pipe(rename('index.min.js')) 
    .pipe(gulp.dest('build/js'));
 });
gulp.task('default',['rename']);
//關於gulp-rename的更多強大的用法請參考https://www.npmjs.com/package/gulp-rename

2.gulp-uglify(JS壓縮)

安裝:npm install --save-dev gulp-uglify

仍是上面的gulpfile.js代碼以下:

var gulp = require('gulp'),
    rename = require('gulp-rename'),
    uglify = require("gulp-uglify");
 
gulp.task('rename', function () {
    gulp.src('src/**/*.js')
    .pipe(uglify())  //壓縮
    .pipe(rename('index.min.js')) 
    .pipe(gulp.dest('build/js'));
});
gulp.task('default',['rename']);

3.gulp-minify-css(css文件壓縮)

安裝:npm install --save-dev gulp-minify-css

代碼以下:

var gulp = require('gulp'),
   minifyCss = require("gulp-minify-css");
gulp.task('minify-css', function () {
    gulp.src('src/**/*.css') // 要壓縮的css文件
    .pipe(minifyCss()) //壓縮css
    .pipe(gulp.dest('build'));
});
gulp.task('default',['minify-css']);

4.gulp-minify-html(html壓縮)

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

代碼以下:

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

gulp.task('default',['minify-html']);

5.gulp-concat(JS文件合併)

安裝:npm install --save-dev gulp-concat

代碼以下:

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

gulp.task('default',['concat']);

6.gulp-less (less編譯)

安裝:npm install –save-dev  gulp-less

Gulpfile.js代碼以下:

var gulp = require('gulp'),
    less = require("gulp-less");
 
gulp.task('compile-less', function () {
    gulp.src('src/less/*.less')
    .pipe(less())
    .pipe(gulp.dest('build/css'));
});

gulp.task('default',['compile-less']);

7.gulp-sass(sass編譯)

安裝:npm install –save-dev  gulp-sass

代碼以下:

var gulp = require('gulp'),
    sass = require("gulp-sass");
 
gulp.task('compile-sass', function () {
    gulp.src('src/sass/*.sass')
    .pipe(sass())
    .pipe(gulp.dest('build/css'));
});

gulp.task('default',['compile-sass']);

8.gulp-imagemin(圖片壓縮)

安裝:npm install –save-dev  gulp-imagemin

代碼以下:

var gulp = require('gulp');
var imagemin = require('gulp-imagemin');

gulp.task('uglify-imagemin', function () {
    return gulp.src('src/images/*')
        .pipe(imagemin())
        .pipe(gulp.dest('build/images'));
});
gulp.task('default',['uglify-imagemin']);

9.理解 Browserify

   browserify是一個使用node支持的CommonJS模塊標準 來爲瀏覽器編譯模塊的,能夠解決模塊及依賴管理;

先來看看使用gulp常見的問題:

1. 使用 gulp 過程當中,偶爾會遇到 Streaming not supported 這樣的錯誤。這一般是由於常規流與 vinyl 文件對象流有差別、
gulp 插件默認使用了只支持 buffer (不支持 stream)的庫。好比,不能把 Node 常規流直接傳遞給 gulp 及其插件。
好比以下代碼:會拋出異常的;

var gulp = require('gulp');
var uglify = require('gulp-uglify');
var concat = require('gulp-concat');
var rename = require('gulp-rename');
var fs = require('fs'); 

gulp.task('bundle', function() {  
  return fs.createReadStream('./test.txt')
    .pipe(uglify())
    .pipe(rename('bundle.min.js'))
    .pipe(gulp.dest('dist/'));
});
gulp.task('default',['bundle']);

gulp 選擇默認使用內容轉換成 buffer 的 vinyl 對象流,以方便處理。固然,設置 buffer: false 選項,可讓 gulp 禁用 buffer:
好比以下gulpfile.js代碼以下:

var gulp = require('gulp');
var fs = require('fs'); 
gulp.task('bundle', function() { 
    return gulp.src('./src/js/app.js', {buffer: false}).on('data', function(file) {  
      var stream = file.contents;
      stream.on('data', function(chunk) {
        console.log('Read %d bytes of data', chunk.length);
      });
    });
})
gulp.task('default',['bundle']);

運行以下:

2. Stream 和 Buffer 之間轉換

基於依賴的模塊返回的是 stream, 以及 gulp 插件對 stream 的支持狀況,有時須要把 stream 轉換爲 buffer。好比不少插件只支持 buffer,如 gulp-uglify、使用時能夠經過 gulp-buffer 轉換:Stream轉換Buffer 
以下gulpfile.js代碼:

var gulp = require('gulp');
    var source = require('vinyl-source-stream');  
    var buffer = require('gulp-buffer');  
    var uglify = require('gulp-uglify');
    var fs = require('fs'); 
    gulp.task('bundle', function() {  
        return fs.createReadStream('./src/js/app.js')  
          .pipe(source('app.min.js')) // 常規流轉換成 vinyl 對象
          .pipe(buffer())
          .pipe(uglify())
          .pipe(gulp.dest('dist/'));
    })
    gulp.task('default',['bundle']);

以下所示:

3. 從 Buffer 到 Stream之間轉換

也能夠經過使用 gulp-streamify(https://www.npmjs.com/package/gulp-streamify) 或者 gulp-stream (https://www.npmjs.com/package/gulp-stream)插件,讓只支持 buffer 的插件直接處理 stream。
以下gulpfile.js代碼:

var gulp = require('gulp');
    var wrap = require('gulp-wrap');  
    var streamify = require('gulp-streamify');  
    var uglify = require('gulp-uglify');  
    var gzip = require('gulp-gzip');

    gulp.task('bundle', function() { 
        return gulp.src('./src/js/app.js', {buffer: false})  
              .pipe(wrap('(function(){<%= contents %>}());'))
              .pipe(streamify(uglify()))
              .pipe(gulp.dest('dist'))
              .pipe(gzip())
              .pipe(gulp.dest('dist'));
    });
    gulp.task('default',['bundle']);

以下所示:

4. 使用browserify進行Stream 向 Buffer 轉換

vinyl-source-stream + vinyl-buffer
vinyl-source-stream(https://www.npmjs.com/package/vinyl-source-stream) : 將常規流轉換爲包含 Stream 的 vinyl 對象;
vinyl-buffer(https://www.npmjs.com/package/vinyl-buffer) 將 vinyl 對象內容中的 Stream 轉換爲 Buffer。

gulpfile.js代碼以下:

var browserify = require('browserify');  
    var gulp = require('gulp');  
    var uglify = require('gulp-uglify');  
    var source = require('vinyl-source-stream');  
    var buffer = require('vinyl-buffer');

    gulp.task('browserify', function() {  
      return browserify('./src/js/app.js')
        .bundle()
        .pipe(source('bundle.js'))   // gives streaming vinyl file object
        .pipe(buffer())              // convert from streaming to buffered vinyl file object      
        .pipe(uglify())   
        .pipe(gulp.dest('./dist/js'));
    });
    gulp.task('default',['browserify']);

vinyl-source-stream 使用指定的文件名bundle.js建立了一個 vinyl 文件對象實例,所以能夠再也不使用 gulp-rename(gulp.dest 將用此文件名寫入結果)。

以下所示:

5. 使用browserify多文件操做

    5-1. 使用Gulp和Browserify單個文件操做也能夠以下:

var gulp = require('gulp');  
var browserify = require('browserify');  
var source = require('vinyl-source-stream');  

gulp.task('browserify', function(){
     return browserify(
        {entrieis:['./src/js/app.js']})
        .bundle()
        .pipe(source("bundle.js"))
        .pipe(gulp.dest('dist'));
});
gulp.task('default',['browserify']);

  5-2 多文件操做以下:

var gulp       = require('gulp');
var source     = require('vinyl-source-stream');
var browserify = require('browserify');
var glob       = require('glob');
var es         = require('event-stream');

gulp.task('browserify', function(done) {
     glob('./src/**/*.js', function(err, files) {
      if(err) {
        done(err)
      };
       var tasks = files.map(function(entry) {
            return browserify({ entries: [entry] })
              .bundle()
              .pipe(source(entry))
               .pipe(gulp.dest('./dest'));
         });
        es.merge(tasks).on('end', done);
       })
});
gulp.task('default',['browserify']);

5-3 也可使用gulp.src和browserify一塊兒使用;代碼以下:

var gulp       = require('gulp');
var source     = require('vinyl-source-stream');
var browserify = require('browserify');
var glob       = require('glob');
var es         = require('event-stream');
var buffer = require('vinyl-buffer');

gulp.task('browserify', function(done) {
    gulp.src('./src/**/*.js',function(err,files) {
        if(err) {
            done(err)
        }
        files.forEach(function(file){
            return browserify({ entries: [file] })
                .bundle()
                .pipe(source(file))
                .pipe(buffer()) 
                .pipe(gulp.dest('./dest'));
        });
       });
});
gulp.task('default',['browserify']);

browserify深刻學習;

1.前言:
以前咱們作項目的時候,好比須要jquery框架的話,咱們可能須要下載jquery源碼,而後引入到咱們的項目中,以後在html文件中像以下引入便可:
<script src="path/to/jquery.js"></script>

2.bower學習
以後咱們學習了 Bower,所以咱們安裝了Bower,而後進入咱們的項目文件根目錄中 在命令行中使用bower安裝jquery;以下命令:
bower install jquery
以後會在咱們的根目錄中生成一個 bower_components文件,裏面包含了jquery文件,所以咱們須要在咱們的html文件中這樣引入jquery了;
<script src="bower_components/jquery/dist/jquery.js"></script>

以下所示:

3. npm&Browserify學習
咱們如今又可使用 命令行用npm安裝jQuery。進入項目的根目錄後,運行以下命令:
npm install --save-dev jquery

接着咱們在命令行中全局安裝 browserify;命令以下:
sudo npm install -g browserify
如今咱們就能夠在命令行中使用 browserify命令了;
好比如今我在個人項目目錄下的源文件 src/js/a.js 下想要使用jquery的話,咱們能夠在a.js代碼以下引用:

var $ = require('jquery');
$(function(){
// 獲取頁面中的DOM元素
console.log($("#jquery2"));
});
function a() {
console.log("a.js");
}
a();

再進入命令行相對應的js文件中,進行以下命令:
browserify a.js -o dest.js
執行命令後會在同目錄下生成dest.js,該文件包含jquery.js和a.js;而後咱們把dest文件引入到咱們須要的html文件中便可訪問;

4. gulp和Browserify 一塊兒使用
結合gulp一塊兒使用時,咱們只須要把Browserify安裝到咱們的項目內便可;所以進入項目的根目錄中,進行以下命令安裝:
npm install --save-dev browserify

而後在項目的根目錄中在gulpfile.js文件中加入以下代碼:

var gulp = require("gulp");
var browserify = require("browserify");
var sourcemaps = require("gulp-sourcemaps");
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');

gulp.task("browserify", function () {
    var b = browserify({
        entries: "./src/js/a.js",
        debug: true
    });
    return b.bundle()
        .pipe(source("bundle.js"))
        .pipe(buffer())
        .pipe(sourcemaps.init({loadMaps: true}))
        .pipe(sourcemaps.write("."))
        .pipe(gulp.dest("./dist"));
});

gulp.task('default',['browserify']);

a.js代碼仍是以下:

var $ = require('jquery');
$(function(){
    // 獲取頁面中的DOM元素
    console.log($("#jquery2"));
});
function a() {
    console.log("a.js");
}
a();

進入項目的根目錄中運行命令 gulp便可,在目錄中會生成dist目錄(包含bundle.js和bundle.js.map)兩個文件;以後在html文件頁面上引入
dist目錄文件下的bundle.js便可;

在上面的代碼中,debug: true是告知Browserify在運行同時生成內聯sourcemap用於調試。
若是咱們把debug設置成false的話;在瀏覽器中訪問頁面,能夠看到以下:

若是咱們把debug設置成true的話,在瀏覽器中訪問頁面,能夠看到以下:

引入gulp-sourcemaps並設置loadMaps: true是爲了讀取上一步獲得的內聯sourcemap,並將其轉寫爲一個單獨的sourcemap文件。
若是咱們把loadMaps設置成false的話,咱們在瀏覽器訪問頁面以下圖所示:

若是咱們把loadMaps設置成true的話,咱們在瀏覽器訪問頁面以下圖所示:

vinyl-source-stream用於將Browserify的bundle()的輸出轉換爲Gulp可用的[vinyl][](一種虛擬文件格式)流。
vinyl-buffer用於將vinyl流轉化爲buffered vinyl文件(gulp-sourcemaps及大部分Gulp插件都須要這種格式)。

若是代碼比較多,可能一次編譯須要很長時間。這個時候,咱們可使用[watchify][]。它能夠在你修改文件後,
只從新編譯須要的部分(而不是Browserify本來的所有編譯),這樣,只有第一次編譯會花些時間,此後的即時變動刷新則十分迅速。
以下代碼:

var watchify = require('watchify');
var browserify = require('browserify');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var gutil = require('gulp-util');
var sourcemaps = require('gulp-sourcemaps');
var assign = require('lodash.assign');

// 在這裏添加自定義 browserify 選項
var customOpts = {
  entries: ['./src/js/a.js'],
  debug: true
};
var opts = assign({}, watchify.args, customOpts);
var b = watchify(browserify(opts));

// 在這裏加入變換操做
// 好比: b.transform(coffeeify);

gulp.task('js', bundle); // 這樣你就能夠運行 `gulp js` 來編譯文件了
b.on('update', bundle); // 當任何依賴發生改變的時候,運行打包工具
b.on('log', gutil.log); // 輸出編譯日誌到終端

function bundle() {
  return b.bundle()
    // 若是有錯誤發生,記錄這些錯誤
    .on('error', gutil.log.bind(gutil, 'Browserify Error'))
    .pipe(source('bundle.js'))
    // 可選項,若是你不須要緩存文件內容,就刪除
    .pipe(buffer())
    // 可選項,若是你不須要 sourcemaps,就刪除
    .pipe(sourcemaps.init({loadMaps: true})) // 從 browserify 文件載入 map
       // 在這裏將變換操做加入管道
    .pipe(sourcemaps.write('./')) // 寫入 .map 文件
    .pipe(gulp.dest('./dist'));
}
gulp.task('default',['js']);

5. 使用Browserify來組織JavaScript文件

仍是上面那個項目,假如src/js文件內有2個js文件,分別爲a.js和b.js;假如如今a.js想引用b.js的模塊,就像seajs那樣經過require來引用如何作?
如今咱們能夠在b.js這樣編寫代碼;把咱們的代碼模塊經過module.exports 或 exports模塊對外提供接口,和其餘的好比seajs同樣編寫代碼
便可:好比如今b.js代碼以下:
function b() {
    console.log("b.js");
}
module.exports = b;
那麼a.js代碼以下:
var b = require('./b');
function a() {
   b();
   console.log("a.js");
}
a();
而後再在gulpfile.js文件代碼仍是以下:

var gulp = require("gulp");
var browserify = require("browserify");
var sourcemaps = require("gulp-sourcemaps");
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');

gulp.task("browserify", function () {
    var b = browserify({
        entries: "./src/js/a.js",
        debug: true
    });
    return b.bundle()
        .pipe(source("bundle.js"))
        .pipe(buffer())
        .pipe(sourcemaps.init({loadMaps: true}))
        .pipe(sourcemaps.write("."))
        .pipe(gulp.dest("./dist"));
});

gulp.task('default',['browserify']);

在命令行中運行gulp,便可生成bundle.js文件;引用該文件便可解決模塊依賴的問題;咱們打開bundle.js文件查看代碼以下:

(function e(t,n,r){
    /*
    function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;
    if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");
    throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];
        return s(n?n:e)},l,l.exports,e,t,n,r)}
        return n[o].exports}var i=typeof require=="function"&&require;
        for(var o=0;o<r.length;o++)s(r[o]);return s */

})({1:[function(require,module,exports){

var b = require('./b');

function a() {
    b();
    console.log("a.js");
}
a();
},{"./b":2}],2:[function(require,module,exports){
function b() {
    console.log("b.js");
}
module.exports = b;
},{}]},{},[1])

該函數有3個參數,
第一個參數是一個對象;第二個參數是一個空對象{};第三個參數是一個[1];
第一個參數是一個object;它的每個key都是一個數字,做爲模塊的id,每個數字key對應的值是長度爲2的數組。能夠看下,第一個key數字1
模塊中的數組中的第一個元素是a.js代碼;數組中第二個元素是a模塊的依賴項,第二個key數字2模塊中數組第一個元素是b.js代碼;數組中的第二個
元素是空對象{};由於b模塊沒有依賴項;所以爲{};
咱們的文件代碼經過一個匿名函數被包裝起來,這樣作的好處是:咱們的瀏覽器中並無require這樣的解決依賴的東西,可是咱們又想像seajs,
requireJS等同樣使用require來引入文件解決模塊依賴的文件的時候,所以 Browserify實現了require、module、exports這3個關鍵字。
第2個參數幾乎老是空的{}。它若是有的話,也是一個模塊map;
第3個參數是一個數組,指定的是做爲入口的模塊id。a.js是入口模塊,它的id是1,因此這裏的數組就是[1]。
那麼 Browserify是如何實現了require、module、exports這3個關鍵字的呢?
咱們前面被註釋的代碼將解析require、module、exports這三個3個參數,而後讓一切運行起來。
這段代碼是一個函數,來自於browser-pack項目的[prelude.js][]。

上面咱們看到在Browserify打包文件的時候,它會自動使用匿名函數進行包裝;所以咱們在編寫代碼的時候通常能夠不須要考慮全局變量的問題了;
不須要在函數中添加像類型匿名函數的結構 (function(){})();

10.理解gulp.watch()的使用

gulp.watch()方法能夠監聽文件的動態修改,它接受一個glob或者glob數組(和gulp.src()同樣)以及一個任務數組來執行回調。下面咱們來看下
gulp.watch()方法的使用;好比如今gulpfile.js任務代碼以下:

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

var paths = {
  scripts: ['src/js/**/*.js'],
  css: ['src/css/**/*.css'],
  // 把源文件html放在src下,會自動打包到指定目錄下
  html: ['src/html/**/*.html']
};

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

  return gulp.src(paths.scripts)
    .pipe(concat('all.js'))
    .pipe(uglify())
    .pipe(gulp.dest('build/js'));
});

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

  return gulp.src(paths.css)
    .pipe(concat('all.css'))
    .pipe(gulp.dest('build/css'));
});

// 監聽html文件的改變
gulp.task('html',function(){
    return gulp.src(paths.html)
        .pipe(gulp.dest('html/'));
});

// Rerun the task when a file changes
gulp.task('watch', function() {
  gulp.watch(paths.scripts, ['scripts']);
  gulp.watch(paths.css, ['css']);
  gulp.watch(paths.html, ['html']);
});
 
// The default task (called when you run `gulp` from cli)
gulp.task('default', ['scripts', 'css', 'html','watch']);

監聽src文件下的js和css及html的文件的變化,咱們在相關的項目根目錄命令行中運行gulp後,當咱們改變css或者js文件或html的時候,
能夠監聽文件的動態修改,所以保存刷新瀏覽器便可生效,這是gulp-watch的基本功能;以下所示:

會把src下的文件css和js自動打包到build下,src下的html文件會打包到項目根目錄下的html文件下;
上面的gulp.watch 回調函數有一個包含觸發回調函數信息的event對象:好比咱們把gulp watch任務改爲以下:
當每次更改文件的時候 都會觸發change事件;代碼改成以下:

gulp.task('watch', function() {
  var watch1 = gulp.watch(paths.scripts, ['scripts']);
  var watch2 = gulp.watch(paths.css, ['css']);
  var watch3 = gulp.watch(paths.html, ['html']);

  watch1.on('change', function (event) {
       console.log('Event type: ' + event.type); // Event type: changed
       console.log('Event path: ' + event.path); // Event path: /Users/tugenhua/gulp/src/css/a.css
  });
  watch2.on('change', function (event) {
       console.log('Event type: ' + event.type); // Event type: changed
       console.log('Event path: ' + event.path); // Event path: /Users/tugenhua/gulp/src/css/a.css
  });
  watch3.on('change', function (event) {
       console.log('Event type: ' + event.type); // Event type: changed
       console.log('Event path: ' + event.path); // Event path: /Users/tugenhua/gulp/src/css/a.css
  });
});

除了change事件,還能夠監聽不少其餘的事件:
end 在watcher結束時觸發
error 在出現error時觸發
ready 在文件被找到並正被監聽時觸發
nomatch 在glob沒有匹配到任何文件時觸發

Watcher對象也包含了一些能夠調用的方法:
watcher.end() 中止watcher(以便中止執行後面的任務或者回調函數)
watcher.files() 返回watcher監聽的文件列表
watcher.add(glob) 將與指定glob相匹配的文件添加到watcher
watcher.remove(filepath) 從watcher中移除個別文件

上面是經過gulp-watch來動態監聽html,css和js文件的改變,可是須要從新刷新頁面才能生效;

11.理解LiveReload插件的使用

該插件的做用是當文件被修改的時候,它能實時刷新網頁,這樣的話就不須要咱們實時刷新了;可是該插件須要在咱們服務器下生效;所以
咱們須要使用 gulp-connect 建立一個服務器;下面是gulpfile.js代碼以下;使用liveReload實現實時刷新;

var gulp = require('gulp');
var connect = require('gulp-connect');
var uglify = require("gulp-uglify");
var concat = require("gulp-concat");
var mincss = require("gulp-minify-css");  
//自動刷新     
var livereload = require("gulp-livereload");

/* 設置路徑 */
var paths = {
  src   : "src/",
  css   : "src/css/",
  scripts    : "src/js/",
  scss  : "src/scss/",
  img   : "src/images/",
  html  : "src/html/", 
  build : "build"
}
// 建立一個webserver 服務器
gulp.task('webserver', function() {
    connect.server({
        port: 8000,
        livereload: true
    });
});

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

  return gulp.src(paths.scripts+ "**/*.js")
    .pipe(concat('all.js'))
    .pipe(uglify())
    .pipe(gulp.dest(paths.build + '/js'));
});

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

  return gulp.src(paths.css+ "**/*.css")
    .pipe(concat('all.css'))
    .pipe(mincss())
    .pipe(gulp.dest(paths.build + '/css'));
});

// 監聽html文件的改變
gulp.task('html',function(){
    return gulp.src(paths.html + "**/*.html")
        .pipe(gulp.dest('html/'));
});

//reload server
gulp.task('reload-dev',['scripts','css','html'],function() {
  return gulp.src(paths.src + '**/*.*')
    .pipe(connect.reload());
});

// Watch
gulp.task('watch', function() {
    //監聽生產環境目錄變化
    gulp.watch(paths.src + '**/*.*',['reload-dev']);
})

gulp.task('default', ['webserver','reload-dev','watch']);

12.理解browser-sync的使用

BroserSync在瀏覽器中展現變化的功能與LiveReload很是類似,可是它有更多的功能。實現靜態服務器,也是能實時刷新頁面的;BrowserSync也能夠在不一樣瀏覽器之間同步點擊翻頁、表單操做、滾動位置等功能。
安裝以下命令:
npm install --save-dev browser-sync
以下gulpfile文件是動態監聽js,css和html文件的變化實時更新;以下代碼:

var gulp = require('gulp');
var connect = require('gulp-connect');
var uglify = require("gulp-uglify");
var concat = require("gulp-concat");
var mincss = require("gulp-minify-css");  
//自動刷新     
var browserSync = require('browser-sync').create();
var reload      = browserSync.reload;

/* 設置路徑 */
var paths = {
  src   : "src/",
  css   : "src/css/",
  scripts    : "src/js/",
  scss  : "src/scss/",
  img   : "src/images/",
  html  : "src/html/", 
  build : "build"
}

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

  return gulp.src(paths.scripts+ "**/*.js")
    .pipe(concat('all.js'))
    .pipe(uglify())
    .pipe(gulp.dest(paths.build + '/js'))
    .pipe(reload({stream:true})); // inject into browsers
});

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

  return gulp.src(paths.css+ "**/*.css")
    .pipe(concat('all.css'))
    .pipe(mincss())
    .pipe(gulp.dest(paths.build + '/css'))
    .pipe(reload({stream:true})); // inject into browsers
});

// 監聽html文件的改變
gulp.task('html',function(){
    return gulp.src(paths.html + "**/*.html")
        .pipe(gulp.dest('html/'))
        .pipe(reload({stream:true})); // inject into browsers
});

// 建立本地服務器,而且實時更新頁面文件
gulp.task('browser-sync', ['scripts','css','html'],function() {
    var files = [
      '**/*.html',
      '**/*.css',
      '**/*.js'
    ];
    
    browserSync.init(files,{
        server: {
            //baseDir: "./html"
        }
    });
    
});

//gulp.task('default', ['webserver','reload-dev','watch']);
gulp.task('default', ['browser-sync'], function () {
    gulp.watch("**/*.css", ['css']);
    gulp.watch("**/*.html", ['html']);
    gulp.watch("**/*.js", ['scripts']);
});

對 browser-sync 更多的學習 請看文檔(http://www.browsersync.cn/docs/api/)

gulp構建小型項目的基本過程

好比我如今一個小項目的基本架構以下所示:

src文件夾:是源文件的目錄結構;build文件夾是經過構建後生成的文件;

src存放文件以下:

   common(該目錄是存放公用的插件css文件和js文件)

   html目錄是存放目前的html文件

   images目錄存放全部在項目中用到的圖片

   js目錄是在項目中用到的全部的js文件;

   less文件是存放須要預編譯成css文件;

這上面幾個目錄都會經過gulpfile.js打包到build目錄下;經過上面的學習browserify(能夠解決js的模塊化依賴問題)及學習 browserSync(實現自動刷新效果),所以目前該項目打包有2個優勢:

 1. 可使用require,exports,和moudle這三個參數實現js模塊化組織及加載的問題,它不須要依賴於seajs或者requireJS;

 2. 能夠實時監聽html,css,js文件的修改,從而不須要刷新頁面,能夠提升工做效率;

如今我把package.json用到的依賴包放到下面來:

{
  "name": "testProject",
  "version": "0.0.1",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "browser-sync": "^2.12.10",
    "browserify": "^13.0.1",
    "event-stream": "^3.3.2",
    "glob": "^7.0.3",
    "gulp": "^3.9.1",
    "gulp-buffer": "0.0.2",
    "gulp-clean": "^0.3.2",
    "gulp-concat": "^2.6.0",
    "gulp-connect": "^4.0.0",
    "gulp-gzip": "^1.3.0",
    "gulp-imagemin": "^3.0.1",
    "gulp-less": "^3.1.0",
    "gulp-livereload": "^3.8.1",
    "gulp-marked": "^1.0.0",
    "gulp-minify-css": "^1.2.4",
    "gulp-rename": "^1.2.2",
    "gulp-sourcemaps": "^1.6.0",
    "gulp-str-replace": "0.0.4",
    "gulp-streamify": "^1.0.2",
    "gulp-uglify": "^1.5.3",
    "gulp-util": "^3.0.7",
    "gulp-watch": "^4.3.6",
    "gulp-wrap": "^0.13.0",
    "lodash.assign": "^4.0.9",
    "node-glob": "^1.2.0",
    "vinyl-buffer": "^1.0.0",
    "vinyl-source-stream": "^1.1.0",
    "watchify": "^3.7.0"
  }
}

項目用到的話,直接npm install 就能夠把全部的包加載進來;

gulpfile.js代碼以下:

var gulp = require('gulp');
var less = require('gulp-less');
var mincss = require('gulp-minify-css');
var concat = require("gulp-concat");
var uglify = require("gulp-uglify");
var clean = require('gulp-clean');

var browserify = require("browserify");
var sourcemaps = require("gulp-sourcemaps");
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');

var replace = require('gulp-str-replace');
var imagemin = require('gulp-imagemin');

//自動刷新     
var browserSync = require('browser-sync').create();
var reload      = browserSync.reload;

var fs = require('fs');
var fileContent = fs.readFileSync('./package.json');
var jsonObj = JSON.parse(fileContent);

var argv = process.argv.pop();
var DEBUGGER = (argv === "-D" || argv === "-d") ? true : false;

/* 基礎路徑 */
var paths = {
  css       :  'src/common/css/',
  less      :  'src/less/',
  scripts   :  "src/js/",
  img       :  "src/images/",
  html      :  "src/html/", 
  build     :  "build",
  src       :  'src' 
}
var resProxy = "項目的真實路徑";
var prefix = "項目的真實路徑"+jsonObj.name;

if(DEBUGGER) {
    resProxy = "http://localhost:3000/build";
    prefix = "http://localhost:3000/build";
}

// 先清理文件
gulp.task('clean-css',function(){
    return gulp.src(paths.build + "**/*.css")
             .pipe(clean());
});
gulp.task('testLess', ['clean-css'],function () {
  return gulp.src([paths.less + '**/*.less',paths.css+'**/*.css']) 
           .pipe(less())
           .pipe(concat('index.css'))
           .pipe(mincss())
           .pipe(replace({
                      original : {
                        resProxy : /\@{3}RESPREFIX\@{3}/g,
                        prefix : /\@{3}PREFIX\@{3}/g
                      },
                      target : {
                        resProxy : resProxy,
                        prefix : prefix
                      }
                  }))
           .pipe(gulp.dest(paths.build + "/css"))
           .pipe(reload({stream:true}));
});

// 監聽html文件的改變
gulp.task('html',function(){
    return gulp.src(paths.html + "**/*.html")
        .pipe(replace({
            original : {
              resProxy : /\@{3}RESPREFIX\@{3}/g,
              prefix : /\@{3}PREFIX\@{3}/g
            },
            target : {
              resProxy : resProxy,
              prefix : prefix
            }
        }))
      .pipe(gulp.dest(paths.build+'/html'))
      .pipe(reload({stream:true})); 
});
// 對圖片進行壓縮
gulp.task('images',function(){
   return gulp.src(paths.img + "**/*")
          .pipe(imagemin())
          .pipe(gulp.dest(paths.build + "/images"));
});
// 建立本地服務器,而且實時更新頁面文件
gulp.task('browser-sync', ['testLess','html','browserify'],function() {
    var files = [
      '**/*.html',
      '**/*.css',
      '**/*.less',
      '**/*.js'
    ]; 
    browserSync.init(files,{
   
        server: {
            //baseDir: "./html"
        }
        
    });
});

// 解決js模塊化及依賴的問題
gulp.task("browserify",function () {
    var b = browserify({
        entries: ["./src/js/index.js"],
        debug: true
    });
    return b.bundle()
        .pipe(source("index.js"))
        .pipe(buffer())
        .pipe(sourcemaps.init({loadMaps: true}))
        .pipe(gulp.dest("./build/js"))
        .pipe(uglify())
        .pipe(sourcemaps.write("."))
        .pipe(replace({
                original : {
                  resProxy : /\@{3}RESPREFIX\@{3}/g,
                  prefix : /\@{3}PREFIX\@{3}/g
                },
                target : {
                  resProxy : resProxy,
                  prefix : prefix
                }
            }))
        .pipe(gulp.dest("./build/js"))
        .pipe(reload({stream:true}));
});

gulp.task('default',['testLess','html','images','browserify'],function () {
    gulp.watch(["**/*.less","**/*.css"], ['testLess']);
    gulp.watch("**/*.html", ['html']);
    gulp.watch("**/*.js", ['browserify']);
});

gulp.task('server', ['browser-sync','images'],function () {
    gulp.watch(["**/*.less","**/*.css"], ['testLess']);
    gulp.watch("**/*.html", ['html']);
    gulp.watch("**/*.js", ['browserify']);
});

若是咱們在命令行中 運行gulp server -d  那就是開發環境,會自動打開一個服務器;若是運行gulp的話,就是線上的正式環境了;代碼會經過replace插件替換成線上的環境;

使用replace插件的好處能夠這樣引入文件

<script src="@@@PREFIX@@@/js/index.js"></script>
<link rel="stylesheet" href="@@@PREFIX@@@/css/index.css"/>

全部的圖片均可以使用這樣的@@@PREFIX@@@來引入的,這樣的話就能夠指向本地的環境和線上的環境了;方便開發;

git項目demo

相關文章
相關標籤/搜索