標籤:des style blog http ar io color os 使用
也算是用了半年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的配置——略複雜和混亂,鄙人口才也不好,便草草幾句話帶過,相信讀者可能也不太能夠理清頭緒。另外Gruntfile.js中已經寫明了任務所依賴的組件,但還要多此一舉配置一份package.json也是匪夷所思。
相比之下,gulp好太多了,只需要一份 gulpfile.js 即可,而且多虧了gulp的流式管道化處理,起配置寫法也算簡潔、統一了許多
為你的項目搭建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
安裝過程可能會遇到組件被牆導致串連不上鏡像的問題,解決方案有兩個,一個是FQ,另一個是走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(sourcemaps.write(‘/‘)) .pipe(mincss()) .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物件導向的思想,歡迎大家去看一看。
共勉~
是時候擱置Grunt,耍一耍gulp了