摘要:我們創建一個可讀流,并嘗試使用和來進行轉換,將最后得到的內容交給。它重新使用可讀流中的文件名,然后在必要時創建文件夾使用。使用常規可讀流時,你可以監聽事件來檢測數據碎片的到來不同的是,使用會將轉換成的文件對象重新寫入到流中。
本文翻譯自Getting gulpy -- Advanced tips for using gulp.js
感受過gulp.js帶來的興奮過后,你需要的不僅僅是它的光鮮,而是切切實實的實例。這篇文章討論了一些使用gulp.js時常踩的坑,以及一些更加高級和定制化的插件和流的使用技巧。
基本任務gulp的基本設置擁有非常友好的語法,讓你能夠非常方便的對文件進行轉換:
gulp.task("scripts", function() { return gulp.src("./src/**/*.js") .pipe(uglify()) .pipe(concat("all.min.js")) .pipe(gulp.dest("build/")); });
這種方式能夠應付絕大多數情況,但如果你需要更多的定制,很快就會遇到麻煩了。這篇將介紹這其中的一些情況并提供解決方案。
流不兼容?使用gulp時,你可能會陷入“流不兼容”的問題。這主要是因為常規流和Vinyl文件對象有差異,或是使用了僅支持buffer(不支持流)庫的gulp插件與常規流不兼容。
比如說,你不能直接將常規流與gulp和(或)gulp插件相連。我們創建一個可讀流,并嘗試使用gulp-uglify和gulp-rename來進行轉換,將最后得到的內容交給gulp.dest()。下面就是個錯誤的例子:
var uglify = require("gulp-uglify"), rename = require("gulp-rename"); gulp.task("bundle", function() { return fs.createReadStream("app.js") .pipe(uglify()) .pipe(rename("bundle.min.js")) .pipe(gulp.dest("dist/")); });
為什么我們不能將可讀流和一個gulp插件直接相連?gulp難道不就是一個基于流的構建系統嗎?是的,但上面的例子忽視了一個事實,gulp插件期望的輸入是Vinyl文件對象。你不能直接將一個可讀流與一個以Vinyl文件對象作為輸入的函數(插件)相連
Vinyl文件對象gulp使用了vinyl-fs,它實現了gulp.src()和gulp.dest()方法。vinyl-fs使用vinyl文件對象——一種“虛擬文件格式”。如果我們需要將gulp和(或)gulp插件與常規的可讀流一起使用,我們就需要先把可讀流轉換為vinyl。
使用vinyl-source-stream是個不錯的選擇,如下:
var source = require("vinyl-source-stream"), marked = require("gulp-marked"); fs.createReadStream("*.md") .pipe(source()) .pipe(marked()) .pipe(gulp.dest("dist/"));
另外一個例子首先通過browserify封裝并最終將其轉換為一個vinyl流:
var browserify = require("browserify"), uglify = require("gulp-uglify"), source = require("vinyl-source-stream"); gulp.task("bundle", function() { return browserify("./src/app.js") .bundle() .pipe(source(‘bundle.min.js)) .pipe(uglify()) .pipe(gulp.dest("dist/")); });
哎呦不錯哦。注意我們不再需要使用gulp-rename了,因為vinyl-source-stream創建了一個擁有指定文件名的vinyl文件實例(這樣gulp.dest方法將使用這個文件名)
gulp.dest這個gulp方法創建了一個可寫流,它真的很方便。它重新使用可讀流中的文件名,然后在必要時創建文件夾(使用mkdirp)。在寫入操作完成后,你能夠繼續使用這個流(比如:你需要使用gzip壓縮數據并寫入到其他文件)
流和buffer既然你有興趣使用gulp,這篇文章假設你已經了解了流的基礎知識。無論是buffer還是流,vinyl的虛擬文件都能包含在內。使用常規可讀流時,你可以監聽data事件來檢測數據碎片的到來:
fs.createReadStream("/usr/share/dict/words").on("data", function(chunk) { console.log("Read %d bytes of data", chunk.length); }); > Read 65536 bytes of data > Read 65536 bytes of data > Read 65536 bytes of data > Read 65536 bytes of data > ...
不同的是,使用gulp.src()會將轉換成buffer的vinyl文件對象重新寫入到流中。也就是說,你獲得的不再是數據碎片,而是將內容轉換成buffer后的(虛擬)文件。vinyl文件格式擁有一個屬性來表示里面是buffer還是流,gulp默認使用buffer:
gulp.src("/usr/share/dict/words").on("data", function(file) { console.log("Read %d bytes of data", file.contents.length); }); > Read 2493109 bytes of data
這個例子說明了在文件被完整加入到流之前數據會被轉換成buffer。
Gulp默認使用buffer盡管更加推薦使用流中的數據,但很多插件的底層庫使用的是buffer。有時候必須使用buffer,因為轉換需要完整的文件內容。比如文本替換和正則表達式的情形。如果使用數據碎片,將會面臨匹配失敗的風險。同樣,像UglifyJS和Traceur Compiler需要輸入完整的文件內容(至少需要語法完整的JavaScript字符串)
這就是為什么gulp默認使用轉換成buffer的流,因為這更好處理。
使用轉換成buffer的流也有缺點,處理大文件時將非常低效。文件必須完全讀取,然后才能被加入到流中。那么問題來了,文件的尺寸多大才會降低性能?對于普通的文本文件,比如JavaScript、CSS、模板等等,這些使用buffer開銷非常小。
在任何情況下,如果將buffer選項設為false,你可以告訴gulp流中傳遞的內容究竟是什么。如下所示:
gulp.src("/usr/share/dict/words", {buffer: false}).on("data", function(file) { var stream = file.contents; stream.on("data", function(chunk) { console.log("Read %d bytes of data", chunk.length); }); }); > Read 65536 bytes of data > Read 65536 bytes of data > Read 65536 bytes of data > Read 65536 bytes of data > ...從流到buffer
由于所需的輸入(輸出)流和gulp插件不盡相同,你可能需要將流轉換成buffer(反之亦然)。之前已經有過介紹,大多數插件使用buffer(盡管他們的一部分也支持流)。比如gulp-uglify和gulp-traceur。你可以通過gulp-buffer來轉換成buffer:
var source = require("vinyl-source-stream"), buffer = require("gulp-buffer"), uglify = require("gulp-uglify"); fs.createReadStream("./src/app.js") .pipe(source("app.min.js")) .pipe(buffer()) .pipe(uglify()) .pipe(gulp.dest("dist/"));
或者另一個例子:
var buffer = require("gulp-buffer"), traceur = require("gulp-traceur"); gulp.src("app.js", {buffer: false}) .pipe(buffer()) .pipe(traceur()) .pipe(gulp.dest("dist/"));將buffer轉換為流
你也可以使用gulp-streamify或gulp-stream將一個使用buffer的插件的輸出轉化為一個可讀流。這樣處理之后,跟在使用buffer的插件后面的(只能)使用流的插件也能正常工作了。
var wrap = require("gulp-wrap"), streamify = require("gulp-streamify"), uglify = require("gulp-uglify"), gzip = require("gulp-gzip"); gulp.src("app.js", {buffer: false}) .pipe(wrap("(function(){<%= contents %>}());")) .pipe(streamify(uglify())) .pipe(gulp.dest("build")) .pipe(gzip()) .pipe(gulp.dest("build"));不是所有事都需要插件
雖然已經有很多使用且方便的插件,很多任務以及轉換可以不使用插件而輕易完成。插件會帶來一些問題,你需要依賴一個額外的npm模塊,一個插件接口和(反應遲鈍?)的維護者,等等。如果一個任務可以不使用插件而使用原生模塊就能輕易完成,絕大多數情況下,都建議不要使用插件。能夠理解上面所說的概念,并能夠在所處的情況下做出正確的決定,這點非常重要。下面來看一些例子:
vinyl-source-stream之前的例子中,我們已經直接使用了browserify,而不是使用(現已加入黑名單)gulp-browserify插件。這里的關鍵是使用vinyl-source-stream(或類似的庫)進行加工,來將常規的可讀流輸入使用vinyl的插件。
文本轉換另一個例子就是基于字符串的變換。這里有一個非常基礎的插件,直接使用了vinyl的buffer:
function modify(modifier) { return through2.obj(function(file, encoding, done) { var content = modifier(String(file.contents)); file.contents = new Buffer(content); this.push(file); done(); }); }
你可以像這樣使用這個插件:
gulp.task("modify", function() { return gulp.src("app.js") .pipe(modify(version)) .pipe(modify(swapStuff)) .pipe(gulp.dest("build")); }); function version(data) { return data.replace(/__VERSION__/, pkg.version); } function swapStuff(data) { return data.replace(/(w+)s(w+)/, "$2, $1"); }
這個插件并沒有完成,而且也不能處理流(完整版本)。然而,這個例子說明,可以很輕易地通過一些基本函數來創建新的變換。through2庫提供了非常優秀的Node流封裝,并且允許像上面那樣使用轉換函數。
任務流程如果你需要去運行一些定制化或動態的任務,了解gulp所使用的Orchestrator模塊會很有幫助。gulp.add方法其實就是Orchestrator.add方法(事實上所有的方法都是從Orchestrator繼承而來的)。但為什么你需要這個?
* 你不想“私有任務”(比如:不暴露給命令行工具)弄亂gulp任務列表。
* 你需要更多的動態的和(或)可重用的子任務。
請注意,gulp(或grunt)并不總是當前情境下的最佳工具。比如說,如果你需要拼接并使用uglify壓縮一系列的JavaScript文件,又或者你需要編譯一些SASS文件,你可能需要考慮使用makefile或npm run,通過命令行來實現。減少依賴,減少配置,才是正解。
閱讀通過npm run來實現任務自動化來了解更多信息。你需要明確通過一系列的“自定義構建”后需要得到什么,而哪個工具最合適。
不過,我覺得gulp是一個偉大的構建系統,我很喜歡使用它,它展現了Node.js中流的強大。
希望這些能夠幫到你!如果你有任何反饋或其他提議,請在評論中告訴我,或者加我的twitter:@webprolific
小廣告:更多內容歡迎來我的博客,共同探討
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/87638.html
摘要:為何選擇引擎微信小游戲推出之后,很多公司也相應的進入到微信小游戲這個領域,現在市場上的游戲開發引擎,如都對小游戲有了很好的兼容性。 1. 為何選擇Laya引擎 微信小游戲推出之后,很多公司也相應的進入到微信小游戲這個領域,現在市場上的游戲開發引擎,如Cocos、Egret、Laya都對小游戲有了很好的兼容性。目前公司技術棧主要是使用Cocos和Laya,經過幾個項目的接觸,考量了引擎在...
摘要:前言月份開始出沒社區,現在差不多月了,按照工作的說法,就是差不多過了三個月的試用期,準備轉正了一般來說,差不多到了轉正的時候,會進行總結或者分享會議那么今天我就把看過的一些學習資源主要是博客,博文推薦分享給大家。 1.前言 6月份開始出沒社區,現在差不多9月了,按照工作的說法,就是差不多過了三個月的試用期,準備轉正了!一般來說,差不多到了轉正的時候,會進行總結或者分享會議!那么今天我就...
摘要:主有前端后端,并加,各一名。本著工欲善其事,必先利其器的理念,一直以來在工作效率這塊,略懷執念一個問題不應該被解決兩次。下圖為開發項目機制所涉及到的插件工欲善其事,必先利其器,語言,框架皆可以歸結為器而不當僅局限于開發工具以及機。 原文鏈接: http://www.jeffjade.com/2016/05/08/106-vue-es6-jade-scss-webpack-gulp/ 一...
閱讀 1342·2021-09-24 10:26
閱讀 3655·2021-09-06 15:02
閱讀 605·2019-08-30 14:18
閱讀 577·2019-08-30 12:44
閱讀 3119·2019-08-30 10:48
閱讀 1936·2019-08-29 13:09
閱讀 1994·2019-08-29 11:30
閱讀 2279·2019-08-26 13:36