前言: gulp
是前端開發過程中對代碼進行構建的工具,是自動化項目的構建利器;她不僅能對網站資源進行優化,而且在開發過程中很多重復的任務能夠使用正確的工具自動完成;使用她,我們不僅可以很愉快的編寫代碼,而且大大提高我們的工作效率。相比于grunt
的頻繁 IO 操作,gulp的流操作,能更快地更便捷地完成構建工作。此處僅記錄初步折騰中所遇點滴以及待解決的點。
Gulp折騰之初探
折騰之戰略上的藐視
回過頭看Gulp的折騰歷程,使用還是非常簡易的。所以戰略上一定要藐視"她";當然戰術上要給予足夠的重視。畢竟要依賴她以及其他各種插件and編輯器等實現前端工程化,組件化,模塊化,便捷化是一個蠻復雜的過程。總之,折騰伊始需要自信就好,折騰過程帶著耐心就好。譬如,想借助gulp壓縮美化下js代碼,寫如下代碼于gulpfile.js即可:
var gulp = require('gulp'),
uglify = require('gulp-uglify');
var gNeedDealJsFile = './js/*.js'; //javascrip代碼存放路徑
var outPut = "./build/"; //指定輸出文件存放目錄
gulp.task('scripts', function() {
return gulp.src(gNeedDealJsFile)
.pipe(uglify())
.pipe(rename({
suffix: '.min'
}))
.pipe(gulp.dest(outPut));
});
在gulpfile.js同級目錄運行gulp scripts
即可;PS:當然前提是您已經安裝了nodejs,并且使用npm安裝了代碼中需要的插件gulp
和gulp-uglify
與本地。
摒棄了gulp.run()
gulp的API很簡單,常用的也就幾個:watch, task, dest, src;目前已經摒棄了run方法。
//創建Default Task:注冊缺省任務
gulp.task('default', function() {
gulp.run('jshint', 'scripts');
gulp.watch(workSpace, function() {
gulp.run('jshint', 'scripts');
})
});
gulp.run() has been deprecated. Use task dependencies or gulp.watch task triggering instead.
可以根據gulp.task(name[, deps], fn)特性寫法替代之。deps:(Array)一個包含任務列表的數組,這些任務會在你當前任務運行之前完成。
注意: 你的任務是否在這些前置依賴的任務完成之前運行了?請一定要確保你所依賴的任務列表中的任務都使用了正確的異步執行方式:使用一個 callback,或者返回一個 promise 或 stream。
//創建Default Task:注冊缺省任務
gulp.task('default', ['jshint' , 'scripts', 'watch']);
折騰gulp.src()
Gulp使用node-glob來從你指定的glob里面獲取文件,這里列舉下面的例子來闡述,方便大家理解:
js/app.js 精確匹配文件
js/.js 僅匹配js目錄下的所有后綴為.js的文件
js//.js 匹配js目錄及其子目錄下所有后綴為.js的文件
!js/app.js 從匹配結果中排除js/app.js,這種方法在你想要匹配除了特殊文件之外的所有文件時非常管用
*.+(js|css) 匹配根目錄下所有后綴為.js或者.css的文件
此外,Gulp也有很多其他的特征,但并不常用。如果你想了解更多的特征,請查看Minimatch文檔。
js目錄下包含了壓縮和未壓縮的JavaScript文件,現在我們想要創建一個任務來壓縮還沒有被壓縮的文件,我們需要先匹配目錄下所有的JavaScript文件,然后排除后綴為.min.js的文件:
gulp.src(['js/**/*.js', '!js/**/*.min.js'])
使用gulp-jshint()
好吧,不同幫派,不同俠士,對于代碼的輸出是不一致的。而這JS又沒像Py那般天生帶有美麗的基因。即便出了jshint這樣的好利器,然,也得考慮下兼顧團隊已有的Style。所以,這里配置得糾結下,微說Gulp之gulp-jshint。
gulp.task('lint', function(){
return gulp.src(workSpace)
.pipe(jshint())
.pipe(jshint.reporter('YOUR_REPOTER_HERE'));
});
這reporter使用"default"的話,就會采用默認蠻嚴格的檢查手段。為了能夠規范而不失靈活的擼起JS,在使用JShint之時,過濾掉哪些不合時宜的寫法(如下寫法就可以不留情的規避之),就得斟酌下咯。列舉些常見不推薦的寫法,運行時提醒如下:
Missing semicolon. (W033) //丟失分號
['lack'] is better written in dot notation //推薦xx.lack寫法,而不是xx['lack']
You might be leaking a variable (disX) here. (W120) //不推薦連等寫法:比如posX = disX = 0;
'status' is defined but never used. (W098) //不推薦:定義之而不用之
Use '!==' to compare with ''. (W041) //不推薦 "!="或者"=="做變量是否相等判斷。
只對發生更改的 js 文件進行語法檢測
更現實的開發場景是, 項目代碼已存在很久,代碼中有大量的不符合 jshint 規范的代碼。 而根據當前的 gulp 配置,每次發生修改,都會全量檢測一遍所有的文件的語法問題,實際上已存在的問題我并不想在本次提交中修復(同時也是其他同事寫的,例如不加分號問題,改動量太大)。
結果就是,一啟動 gulp,嘩嘩的語法錯誤提示,根本找不到自己想看的文件檢測結果。這時修改一個文件,又對所有文件做了一次全量檢測,又是嘩嘩的滿屏錯誤提示。而我只關心當前修改的文件檢測結果。
gulp 項目首頁推薦了一個gulp-cached插件,正好解決了這個問題。
npm install gulp-cached --save-dev
配置如下
var gulp = require('gulp'),
cache = require('gulp-cached'),
jshint = require('gulp-jshint');
gulp.task('jshint', function () {
gulp.src('./src/**/*.js')
.pipe(cache('jshint'))
.pipe(jshint())
.pipe(jshint.reporter('default'));
});
gulp.task('watch', function () {
gulp.watch('./src/**/*.js', ['jshint']);
});
gulp.task('default', ['jshint', 'watch']);
這樣就能清晰的看到剛才發生修改文件的語法檢測結果了,歐耶。
壓縮-合并-重命名-輸出~JS代碼
壓縮:gulp-uglify
,挺好;
Ps: 有一款gulp-minify
(Desc: Minify JavaScript with UglifyJS2),默認壓縮完畢之后會生成一個帶-min
的文件,略煩。
合并:gulp-concat
;指定合并生成名字即可:
.pipe(concat('all.js'))
重命名: gulp-rename
;可以僅僅為名字指定后綴(不改變文件后綴)Like This:
.pipe(rename({suffix: '.min'}))
輸出: gulp自帶方法gulp.dest
。
//outPutPathName: String
.pipe(gulp.dest(outPutPathName));
壓縮~CSS代碼(合并-重命名-輸出同上)
壓縮: gulp-minify-css
;嗯,這個直接將原文件壓縮了,默認沒有改其名字。如果直接輸出到該目錄下,會覆蓋原文件;若要輸出同目錄之下,可改改名字再輸出,Like this:
.pipe(minifycss())
.pipe(rename({suffix: '.min'}))
.pipe(gulp.dest(outPut));
PS:這個插件(默認)也會幫著干掉注釋,過濾掉空類(沒有內容的描述)等等。
gulp-load-plugins模塊
一般情況下,gulpfile.js中的模塊需要一個個加載。
var gulp = require('gulp'),
jshint = require('gulp-jshint'),
uglify = require('gulp-uglify'),
concat = require('gulp-concat');
gulp.task('js', function () {
return gulp.src('js/*.js')
.pipe(jshint())
.pipe(jshint.reporter('default'))
.pipe(uglify())
.pipe(concat('app.js'))
.pipe(gulp.dest('build'));
});
上面代碼中,除了gulp模塊以外,還加載另外三個模塊。
這種一一加載的寫法,比較麻煩。使用gulp-load-plugins
模塊,可以加載package.json文件中所有的gulp模塊。上面的代碼用gulp-load-plugins模塊改寫,就是下面這樣。
var gulp = require('gulp'),
gulpLoadPlugins = require('gulp-load-plugins'),
plugins = gulpLoadPlugins();
gulp.task('js', function () {
return gulp.src('js/*.js')
.pipe(plugins.jshint())
.pipe(plugins.jshint.reporter('default'))
.pipe(plugins.uglify())
.pipe(plugins.concat('app.js'))
.pipe(gulp.dest('build'));
});
上面代碼假設package.json文件包含以下內容。
{
"devDependencies": {
"gulp-concat": "~2.2.0",
"gulp-uglify": "~0.2.1",
"gulp-jshint": "~1.5.1",
"gulp": "~3.5.6"
}
}
gulp插件的快速安裝
談及了基本應用之后,不得不談下如何快速安裝插件(畢竟,合作開發,彼此都能便捷使用,才是王道)。對于所使用的插件可以一個個安裝,同時也可以批量安裝Like This:
npm install gulp-concat gulp-uglify gulp-jshint [...] gulp-rename --save-dev
--save:將保存配置信息至package.json(package.json是nodejs項目配置文件);
-dev:保存至package.json的devDependencies節點,不指定-dev將保存至dependencies節點;
為什么要保存至package.json?因為node插件包相對來說非常龐大,所以不加入版本管理,將配置信息寫入package.json并將其加入版本管理,其他開發者對應下載即可(命令提示符執行npm install
,則會根據package.json下載所有需要的包)。
對于這塊細節可以參看gulp詳細入門教程;大概來講就是可以借助npm init
命令,按照其一步步提示,輸入項目相關信息。完畢之后,加入需要依賴的插件以及版本信息即可(注意:這是一個普通json文件,一定得符合json格式;同時需要安裝的插件版本也得是 <= 線上最新版本)。PS:前文涉及到的插件下載,即可如此配置予以完成:在生成的package.json中加入如下json(2015-10-26日情形):
"devDependencies": {
"gulp-concat": "~2.6.0",
"gulp-uglify": "~1.4.2",
"gulp-jshint": "~1.11.2",
"gulp-rename": "~1.2.2",
"gulp-minify-css": "~1.2.1",
"gulp-cached": "~1.1.0",
"gulp": "~3.9.0"
}
如此,其他開發者只需更新gulpfile.js以及這個package.json,運行npm install
即可下載gulp所依賴插件于本地,(@ο@) 哇~So Cool。
gulp多項目管理(npm link)
要談下多項目管理了;比如這種場景:對于項目非常多而小,而且彼此間相互獨立;伊始,采用在項目根目錄之下gulpfile.js
和package.json(畢竟每個單獨小項目都來一發單獨配置,豈不是要瘋掉了),會將需要的插件down于根目錄下;而在gulpfile.js中控制所要操作的單個小項目路徑。這樣每次切換項目,都要更改下gulpfile.js,即便將這個分離出來寫一個config.js,也得手動去更改,略略有點蛋疼。幸好,gulp有npm link
,哇哦,體貼如你,夫復何求?
對于npm link
的介紹,可以參見@阮一峰npm模塊管理器一文;我們可以將所需的插件,全局環境下載,在單獨項目中link全局環境下插件。如此,不僅可以不用每次都down,而且,依賴的插件需要update,只需一地更新,所link之處都會受益。
具體做法,首先將需要的插件全局環境下Down下來:
npm install gulp gulp-concat gulp-rename gulp-uglify gulp-minify-css gulp-jshint gulp-cached -g
接下來,(如果我們要使用gulp-concat這個模塊)我們進入單獨小項目(eg: gulpTest),使用npm link gulp-concat
命令會去【mac/linux】/usr/local/lib/node_modules/, 【window】D:\UserProfiles\username\AppData\Roaming\npm\node_modules,目錄下查找名叫gulp-concat
的模塊,找到這個模塊后把該的目錄鏈接到 ~/work/**/gulpTest/node_modules/gulp-concat 這個目錄上來。如此,在此項目小項目中的gulpfile.js中也可以加以使用了。當然,npm link
也支持多個參數:
npm link gulp gulp-concat gulp-rename gulp-uglify gulp-minify-css gulp-jshint gulp-cached
PS:這樣使用時,需要注意忽略掉node_modules目錄下的js/css代碼,gulp.src參數數組中可以加入'!node_modules/**/*.+(js|css)'
以過濾之。
注:即便使用npm link
感覺也不是一個特別簡潔的方案。并且在使用的時候還遇到了些許問題: 有提問在@segmentFaultgulp如何管理多項目? 以及@V2EX gulp如何管理多項目;熱心碼友也提出了一些可行的建議,比如:配置多個task,或者干脆使用npm run
(畢竟項目太小),或者采用 fbi
Node.js庫nodejs-fbi,或者采用nodejs 中的 NODE_PATH
@nodejs 中的 NODE_PATH等等;此處有待進一步學習&折騰&擇決; (如有簡潔方案,歡求指點,拜謝)。
gulp-util幸之助
幸虧有gulp-util之協助:在折騰的過程中,難免不會出現奇奇怪怪的問題;然而gulp本身的報錯提示機制真心讓新手的我蛋蛋的憂傷:比如在折騰的過程中壓縮JS代碼就出現Uglify throws Parse error
;可是提示卻...幸好@stackoverflowUglify throws Parse error一個問答中,大牛給予了明燈般的指導:引入gulp-util
;原話如下:
uglify
will parse the script content before minifying it. I suspect that one of thebrowserify
source maps are being included in the stream down touglify
. Anyway to find the problem you can usegulp-util
's log method to handle uglify's exceptions. Example:
...
var gulpUtil = require('gulp-util');
gulp.task('scripts', function() {
...
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(uglify().on('error', gulpUtil.log)) // notice the error event here
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest('./web/js'));
});
實時刷新頁面
gulp-livereload模塊
gulp-livereload
模塊用于自動刷新瀏覽器,反映出源碼的最新變化。它除了模塊以外,還需要在瀏覽器中安裝插件,用來配合源碼變化。
LiveReload結合了瀏覽器擴展(包括Chrome extension),在發現文件被修改時會實時更新網頁。它可以和gulp-watch插件或者前面描述的gulp-watch()函數一起使用。下面有一個gulp-livereload
倉庫中的README文件提到的例子:
var gulp = require('gulp'),
less = require('gulp-less'),
livereload = require('gulp-livereload'),
watch = require('gulp-watch');
gulp.task('less', function() {
gulp.src('less/*.less')
.pipe(watch())
.pipe(less())
.pipe(gulp.dest('css'))
.pipe(livereload());
});
這會監聽到所有與less/*.less相匹配的文件的變化。一旦監測到變化,就會生成css并保存,然后重新加載網頁.
BrowserSync
安裝 BrowserSync
您可以選擇從Node.js的包管理(NPM)庫中 安裝BrowserSync。打開一個終端窗口,運行以下命令:
npm install -g browser-sync
您告訴包管理器下載BrowserSync文件,并在全局下安裝它們,您可以在所有項目(任何目錄)中使用。
當然您也可以結合gulpjs或gruntjs構建工具來使用,在您需要構建的項目里運行下面的命令:
npm install --save-dev browser-sync
啟動 BrowserSync
靜態網站
如果您想要監聽.css文件, 您需要使用服務器模式。 BrowserSync 將啟動一個小型服務器,并提供一個URL來查看您的網站。
// --files 路徑是相對于運行該命令的項目(目錄)
browser-sync start --server --files "css/*.css"
如果您需要監聽多個類型的文件,您只需要用逗號隔開。例如我們再加入一個.html文件
// --files 路徑是相對于運行該命令的項目(目錄)
browser-sync start --server --files "css/*.css, *.html"
// 如果你的文件層級比較深,您可以考慮使用 "**"(表示任意目錄)匹配,任意目錄下任意.css 或 .html文件。
browser-sync start --server --files "**/*.css, **/*.html"
注:在該文件下運行命令,默認需要啟動網站文件:index.html。
動態網站
如果您已經有其他本地服務器環境PHP或類似的,您需要使用代理模式。 BrowserSync將通過代理URL(localhost:3000)來查看您的網站。
// 主機名可以是ip或域名
browser-sync start --proxy "主機名" "css/*.css"
在本地創建了一個PHP服務器環境,并通過綁定Browsersync.cn來訪問本地服務器,使用以下命令方式,Browsersync將提供一個新的地址localhost:3000來訪問Browsersync.cn,并監聽其css目錄下的所有css文件。
browser-sync start --proxy "Browsersync.cn" "css/*.css"
參考博文:BrowserSync,迅捷從免F5開始。
注:使用的時候純路徑比如"Browsersync.cn"尚好著,然而地址后面帶一堆參數時候,就會遇到些問題;暫時還未搞明白以解決之,特注之,待弄懂!!
前端組件html模板化(gulp-tlp2mod)
參考博文:component-html-engine-demo有敘述有Demo,幫了大忙。
為了前端代碼的模塊化,必要將JS邏輯代碼于布局模板代碼分離開來(當然還有CSS以及靜態資源也都當分離);初來乍到,遇到問題了,如何把tpl轉變成js呢?,OK既然用了構建工具,那么自然也有對應的插件吧,果然 gulp-tpl2mod~模板文件轉js插件;再借助require.js, 先使用gulp-tpl2mod把模板轉換成js字符串,然后包裝成一個模塊,再main.js中引用這個模塊就行了,大功告成;如此,項目工程化又前進一步。
折騰之入門所參考文章資料
gulp API 文檔
Gulp入門教程
Gulp開發教程(翻譯)
Gulp:任務自動管理工具
前端構建工具gulp入門教程
gulp plugins 插件介紹
gulp詳細入門教程
你可能感興趣的文章:
Gulp折騰之路(II)