是時候擱置Grunt,耍一耍gulp了


也算是用了半年Grunt,幾個月前也寫過一篇它的入門文章(點此查看),不得不說它是前端項目的一個得力助手。不過技術工具跟語言一樣日新月異,總會有更好用的新的東西把舊的拍死在沙灘上(當然Grunt肯定沒死,gulp也不是多新穎的東西)。

看標題很明顯知道相比Grunt,我會更為推崇gulp,不是說Grunt不好,而是gulp效率更高、健壯性更好,配置也更為簡單,自然也值得我們為它任性一回。

先談談上述提到的gulp的優越性:

一. 性能更高

相對Grunt頻繁的IO操作讀寫,gulp是將項目任務流程以streaming(流)的形式來做管道化處理,這句話怎么理解呢?

比如一個文件A,Grunt按順序會有三個組件任務a、b、c 要對起進行處理,那么流程可能是這樣的:

讀取A → A.a() → 寫A  → 讀取A → A.b() → 寫A  → 讀取A → A.c() → 寫A  → complete/watch...

那么換成gulp又是怎樣的一個過程,它大概是這么處理:

讀取A並轉為流信息 → A.a() → A.b()  → A.c() → 寫A  → complete/watch...

可以看到執行任務的過程,是減少了(3-1)處對文件A的讀/寫操作,減輕了磁盤IO操作負擔,效率自然也得到明顯提升。

至於“”的概念,可以戳這里了解一下。大致是讀取文件時,不按常規那樣把文件一口氣寫入內存中,而是把文件轉為數據流,收到多少數據就處理多少數據,像經過竹筒的涓涓流水那樣,有水源過來了就馬上處理后(你可以假設竹筒在這里有凈水的過濾作用)再送出去,直到水源全部“流”過該竹筒。

那么“管道化”處理又是怎樣的概念?

它很類似jQuery中的鏈式寫法,在nodeJS中管道式方法的api一般為.pipe() ,比如

XXXX.pipe(a())  //處理a任務
     .pipe(b())  //處理b任務
     .pipe(c())  //處理c任務

你也可以理解為上述的涓涓流水經過一排竹筒后,緊接着流向下一個竹筒,一直這樣循環,直到流過最后一個竹筒(當然流水依舊代表數據,竹筒代表任務)

 

二. 健壯性更好

gulp走的是遞歸編譯的解析方式,有助於項目優化的健壯性。打個比方,比如我們使用sass來編寫樣式,其中b.scss引入了_a.scss文件,如果我們修改了_a.scss的內容,gulp會即時更新b.scss對應的b.css編譯文件,但Grunt是基於緩存機制的,故不會重新編譯b.scss文件,導致問題。

 

三. 配置更簡潔

其實之前寫的那篇Grunt入門的文章,我的確不懂到底要如何介紹Gruntfile.js的配置——略復雜和混亂,鄙人口才也不好,便草草幾句話帶過,相信讀者可能也不太能夠理清頭緒。而gulp使用了node的流式管道化(pipe)處理,其配置和寫法變得簡潔、統一了許多,自然也方便理解。

為你的項目搭建gulp任務

搭建gulp其實很簡單,就倆步驟——在項目根目錄下安裝所需組件,並配置一份gulpfile.js。

我們假設我們有一個放在D盤下的項目 D:/project,里面的文件結構是這樣的:

我們打算利用gulp把RAW文件夾下的js和sass文件編譯/壓縮后都輸出到COMPRESS文件夾下。

 

一. 安裝組件

gulp自己有官方的組件推薦/查找頁面(點我進入),就我們上述項目要求而言,我個人推薦下述幾個組件:

  gulp   //這個是必須安裝的,沒有它,其它組件都用不了(注意watch組件直接集成在gulp中了,無需額外安裝watch組件)
  gulp-sass    //編譯sass用的
  gulp-sourcemaps    //編譯sass時生成額外的.map文件用的(有啥用?看我這篇文章
  gulp-mini-css    //壓縮css使用的
  gulp-uglify    //壓縮、混淆js文件用的

組件的安裝很簡單,直接npm安裝即可,比如安裝gulp:

npm install gulp

安裝過程可能會遇到組件被牆導致連接不上鏡像的問題,解決方法有兩個,一個是翻牆,另一個是走cnpm鏡像(點此查閱)。

組件全部安裝完成后會在你的項目根目錄下生成一個node_modules文件夾,用來存放組件模塊:

 

二. gulpfile.js文件配置

如同Grunt需要配置Gruntfile.js文件來告知node我要用什么組件並以怎樣的流程來執行任務,gulp也需要在項目根目錄配置一個gulpfile.js文件。
我們新建一個gulpfile.js文件,先在之中輸入下面內容:

var gulp = require('gulp'),
    sass = require('gulp-sass'),
    mincss = require('gulp-mini-css'),
    sourcemaps = require('gulp-sourcemaps'),
    uglify = require('gulp-uglify');

濃濃的commonJS風,告知node我們要使用哪些組件模塊來完成任務。
接着我們進一步配置文件,告訴node我們具體要以怎樣的流程來執行任務,這是完整的gulpfile.js文件內容:

var gulp = require('gulp'),
    sass = require('gulp-sass'),
    mincss = require('gulp-mini-css'),
    sourcemaps = require('gulp-sourcemaps'),
    uglify = require('gulp-uglify');

var raw_css = './RAW/css',
    com_css = './COMPRESS/css',
    raw_js = './RAW/js',
    com_js = './COMPRESS/js';

gulp.task('sass', function () {
    gulp.src(raw_css+'/**/*.scss')
        .pipe(sourcemaps.init())
        .pipe(sass())
        .pipe(mincss())
.pipe(sourcemaps.write(
'/')) .pipe(gulp.dest(com_css)); }); gulp.task('mincss', function () { gulp.src(com_css+'/**/*.scss') .pipe(mincss()) .pipe(gulp.dest(com_css)); }); gulp.task('minjs', function () { gulp.src(raw_js+'/**/*.js') .pipe(uglify()) .pipe(gulp.dest(com_js)); }); gulp.task('watch', function () { gulp.watch(raw_css+'/**/*.scss',['sass']); gulp.watch(raw_js+'/**/*.js',['minjs']); }); gulp.task('default',function(){ gulp.run('sass','minjs','mincss'); gulp.run('watch'); });

我們拿寫的最長的sass編譯模塊配置代碼塊來注釋說明下:

//每個gulp.task(name, fn)都是一個任務配置模塊,如本代碼段定義了名為"sass"的任務的執行流程
gulp.task('sass', function () {    
    gulp.src(raw_css+'/**/*.scss')   //gulp.src(glob)返回了一個可讀的stream,如此行返回了RAW/css/下的全部(包含子文件夾里的).scss文件流
        .pipe(sourcemaps.init())  //.pipe()管道化執行組件任務,此處調用gulp-sourcemaps的初始化api來處理接收的文件流(方便后續編譯出.map文件)
        .pipe(sass())         //執行gulp-sass組件任務,把.scss文件流編譯為.css文件流
        .pipe(sourcemaps.write('/'))      //調用gulp-sourcemaps的寫入api,額外輸出.map文件流
        .pipe(mincss())     //執行gulp-mini-css組件任務,壓縮所有css文件流
        .pipe(gulp.dest(com_css));   //gulp.dest(glob)返回一個可寫的stream,如此行是將文件流寫入到 COMPRESS/css 里的對應路徑下
});

 接着看看末尾處的兩個代碼段:

gulp.task('watch', function () {  //定義名為"watch"的任務
    gulp.watch(raw_css+'/**/*.scss',['sass']);   //監聽 RAW/css 下的全部.scss文件,若有改動則執行名為'sass'任務
    gulp.watch(raw_js+'/**/*.js',['minjs']);   //監聽 RAW/js 下的全部.js文件,若有改動則執行名為'minjs'任務
});

gulp.task('default',function(){  //每個gulpfile.js里都應當有一個dafault任務,它是缺省任務入口(類似C語言的main()入口),運行gulp的時候實際只是調用該任務(從而來調用其它的任務)
    gulp.run('sass','minjs','mincss');   //gulp.run(tasks)表示運行對應的任務,這里表示執行名為'sass','minjs','mincss'的三個任務
    gulp.run('watch');    //執行'watch'監聽任務
});

上述的兩個步驟做好后,直接輸入運行 gulp 指令即可(我是在webstorm里直接寫命令的):

同時可以看到COMPRESS文件夾下已經有了我們想要的編譯、壓縮后的文件:

另外這時候gulp已轉入監聽模式(雖然不像Grunt那樣有“watch...”的提示),只要你修改了被監聽的文件,任務會立即被執行一遍,無需再手動gulp一次。

用下來還是覺得gulp是個好東西,比Grunt更易上手,效率也更好,不過相較Grunt的“grunt-newer”組件,gulp對應功能的兩個組件“gulp-changed”和"gulp-newer"貌似運行起來都達不到預想效果,比如我希望只針對我修改了的某個sass文件來做任務處理,我做了如下配置:

var gulp = require('gulp'),
    sass = require('gulp-sass'),
    mincss = require('gulp-mini-css'),
    sourcemaps = require('gulp-sourcemaps'),
    changed = require('gulp-changed'),
    uglify = require('gulp-uglify');

var raw_css = './RAW/css',
    com_css = './COMPRESS/css',
    raw_js = './RAW/js',
    com_js = './COMPRESS/js';

gulp.task('sass', function () {
    gulp.src(raw_css+'/*.scss')
        .pipe(changed(raw_css+'/*.scss'))
        .pipe(sourcemaps.init())
        .pipe(sass())
        .pipe(sourcemaps.write('/'))
        .pipe(mincss())
        .pipe(gulp.dest(com_css));
});

gulp.task('mincss', function () {
    gulp.src(com_css+'/*.scss')
        .pipe(changed(com_css))
        .pipe(mincss())
        .pipe(gulp.dest(com_css));
});

gulp.task('minjs', function () {
    gulp.src(raw_js+'/*.js')
        .pipe(changed(com_js))
        .pipe(uglify())
        .pipe(gulp.dest(com_js));
});

gulp.task('watch', function () {
    gulp.watch(raw_css+'/*.scss',['sass']);
    gulp.watch(raw_js+'/*.js',['minjs']);
});

gulp.task('default',function(){
    gulp.run('sass','minjs','mincss');
    gulp.run('watch');
});
View Code

結果發現依舊是整個文件夾下的文件都被做了任務處理。或許是我的配置不正確,若有了解newer或者changed的朋友望能不吝指正。

另外百度fis貌似也是另一個不錯的alternative,跟gulp差不多,而且中文文檔也挺詳細的,之前用過百度的一些東西,感覺質量還是有保障的。

最后推薦下一個大神級朋友的博客,在我們苦口婆心的催產下他終於發表了第一篇文章,介紹的JS面向對象的思想,歡迎大家去看一看。

共勉~

donate


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM