gulp

 1 'use strict';
 2  2 
 3  3 var gulp = require('gulp');
 4  4 
 5  5 //壓縮html
 6  6 var htmlmin = require('gulp-htmlmin');
 7  7 gulp.task('html', function(){
 8  8     gulp.src('./*.html')
 9  9         .pipe(htmlmin({
10 10             collapseWhitespace: true,
11 11               removeComments: true
12 12         }))
13 13         .pipe(gulp.dest('dist'));
14 14 });
15 15 
16 16 //壓縮css
17 17 var cssnano = require('gulp-cssnano');
18 18 gulp.task('style', function(){
19 19     gulp.src(['./css/style.css','./css/piano.css'])
20 20         .pipe(cssnano())
21 21         .pipe(gulp.dest('dist/css'));
22 22 });
23 23 
24 24 //壓縮js
25 25 var uglify = require('gulp-uglify');
26 26 gulp.task('script', function(){
27 27     gulp.src(['./js/common.js','./js/piano.js'])
28 28         .pipe(uglify())
29 29         .pipe(gulp.dest('dist/js'));
30 30 });
31 31 
32 32 //同步代碼變化
33 33 gulp.task('dist', function(){
34 34     gulp.watch('./*.html', ['html']);
35 35     gulp.watch(['./css/style.css','./css/piano.css'], ['style']);
36 36     gulp.watch(['./js/common.js','./js/piano.js'], ['script']);
37 37 });
38 複製代碼

由於gulp依賴於node環境,因此想使用gulp必須先安裝好node。另外gulp自己是一個輕量化內核,自身擁有的api很少,因此不少功能須要依賴插件完成。關於html、css和js代碼壓縮的插件有三個,分別是gulp-htmlmin、gulp-cssnano、gulp-uglify,這三個插件的具體說明能夠在npm的官網npmjs.com上查看。javascript

gulp是基於Nodejs的自動任務運行器, 她能自動化地完成 javascript/coffee/sass/less/html/image/css 等文件的的測試、檢查、合併、壓縮、格式化、瀏覽器自動刷新、部署文件生成,並監聽文件在改動後重復指定的這些步驟。在實現上,她借鑑了Unix操做系統的管道(pipe)思想,前一級的輸出,直接變成後一級的輸入,使得在操做上很是簡單。css

Webpack與Gulp、Grunt區別

 

  Webpack與Gulp、Grunt沒有什麼可比性,它能夠看做模塊打包機,經過分析你的項目結構,找到JavaScript模塊以及其它的一些瀏覽器不能直接運行的拓展語言(Scss,TypeScript等),並將其轉換和打包爲合適的格式供瀏覽器使用。Gulp/Grunt是一種可以優化前端的開發流程的工具,而WebPack是一種模塊化的解決方案,不過Webpack的優勢使得Webpack在不少場景下能夠替代Gulp/Grunt類的工具。html

  他們的工做方式也有較大區別:前端

  Grunt和Gulp的工做方式是:在一個配置文件中,指明對某些文件進行相似編譯,組合,壓縮等任務的具體步驟,工具以後能夠自動替你完成這些任務。java

  Webpack的工做方式是:把你的項目當作一個總體,經過一個給定的主文件(如:index.js),Webpack將從這個文件開始找到你的項目的全部依賴文件,使用loaders處理它們,最後打包爲一個(或多個)瀏覽器可識別的JavaScript文件。node

grunt vs gulp

雖然gulp已經出來好久了,可是一直沒有去使用過。得益於最近項目須要,就嘗試了一下,如下從幾個要點講一下grunt和gulp使用的區別,側重講一下在使用gulp過程當中發現的問題。而兩種工具孰優孰劣由讀者本身判斷。jquery

1. 書寫方式

grunt 運用配置的思想來寫打包腳本,一切皆配置,因此會出現比較多的配置項,諸如option,src,dest等等。並且不一樣的插件可能會有本身擴展字段,致使認知成本的提升,運用的時候要搞懂各類插件的配置規則。
gulp 是用代碼方式來寫打包腳本,而且代碼採用流式的寫法,只抽象出了gulp.src, gulp.pipe, gulp.dest, gulp.watch 接口,運用至關簡單。經嘗試,使用gulp的代碼量能比grunt少一半左右。git


2. 任務劃分

grunt 中每一個任務對應一個最外層配置的key, 大任務能夠包含小任務,以一種樹形結構存在。舉個栗子:github

uglify: {
    one: { src: 'src/a.js', dest: 'dest/a.min.js' }, two: { src: 'tmp/b.js', dest: 'dist/b.min.js' } }

將uglify劃分子任務的好處是,咱們在封裝不一樣的task時能夠分別對'uglify:one'或'uglify:two'進行調用,這對於某些須要在不一樣時間點調用到uglify的task至關有用。npm

gulp 中沒有子任務的概念,對於上面的需求,只能經過註冊兩個task來完成

gulp.task('uglify:one', function(){ gulp.src('src/a.js') .pipe(uglify()) .dest('dest/a.min.js') }); gulp.task('uglify:two', function(){ gulp.src('tmp/b.js') .pipe(uglify()) .dest('dist/b.min.js') });

固然這種需求每每能夠經過調整打包策略來優化,並不須要分解子task,特殊狀況下能夠用這種方法解決。

3. 運行效率

grunt 採用串行的方式執行任務,好比咱們註冊了這樣一個任務:
grunt.register('default', ['concat', 'uglify', 'release'])
grunt是按書寫的順序首先執行cancat,而後是uglify,最後纔是release,一派和諧的氣氛,誰也不招惹誰。而咱們知道某些操做時能夠同步執行的,好比cssmin和uglifyjs。這時grunt沒法經過簡單地更改配置來達到並行執行的效果,一般的作法是手動寫異步task,舉個栗子:

grunt.registerTask('cssmin', 'async cssmin task', function() { var done = this.async(); cssmin(done); });

在cssmin操做完成後傳入done方法告知程序,但這須要插件支持。

gulp 基於並行執行任務的思想,經過一個pipe方法,以數據流的方式處理打包任務,咱們來看這段代碼:

gulp.task('jsmin', function () { gulp.src(['build/js/**/*.js']) .pipe(concat('app.min.js')) .pipe(uglify() .pipe(gulp.dest('dist/js/')); });

程序首先將build/js下的js文件壓縮爲app.min.js, 再進行uglify操做,最後放置於dist/js下。這一系列工做就在一個task中完成,中間沒有產生任何臨時文件。若是用grunt,咱們須要怎樣寫這個任務?那必須是有兩個task配置,一個concat,一個uglify,中間還必須產生一個臨時文件。從這個角度來講,gulp快在中間文件的產生只生成於內存,不會產生多餘的io操做。
再來看看前面的問題,如何並行執行uglify和cssmin?其實gulp自己就是併發執行的,咱們並不須要多什麼多餘多工做,只需

gulp.task('default', ['uglify', 'cssmin']);

gulp該怎麼快就怎麼來,並不會等到uglify再執行cssmin。
是否是以爲gulp秒殺grunt幾條街了呢?且慢,坑還在後面...
首先咱們須要問一個問題,爲何要用併發?
爲了快?那何時能夠快,何時又不能快?
假設咱們有這樣一個任務:

gulp.task('jsmin', ['clean', 'concat']);

須要先將文件夾清空,再進行合併壓縮,根據gulp的併發執行的方式,兩個任務會同時執行,雖然從指令上看是先執行了clean再執行concat,然而clean還沒結束,concat就執行了,致使代碼合併了一些未被清理的文件,這顯然不是咱們想要的結果。
那這個問題有沒有什麼解決方案呢?
gulp官方API給出了這樣的方法:

  • 給出一個提示,來告知 task 何時執行完畢
  • 而且再給出一個提示,來告知一個 task 依賴另外一個 task 的完成

官方舉了這個例子:
讓咱們先假定你有兩個 task,"one" 和 "two",而且你但願它們按照這個順序執行:
1. 在 "one" 中,你加入一個提示,來告知何時它會完成:能夠再完成時候返回一個 callback,或者返回一個 promise 或 stream,這樣系統會去等待它完成。
2. 在 "two" 中,你須要添加一個提示來告訴系統它須要依賴第一個 task 完成。

所以,這個例子的實際代碼將會是這樣:

var gulp = require('gulp'); // 返回一個 callback,所以系統能夠知道它何時完成 gulp.task('one', function(cb) { // 作一些事 -- 異步的或者其餘的 cb(err); // 若是 err 不是 null 或 undefined,則會中止執行,且注意,這樣表明執行失敗了 }); // 定義一個所依賴的 task 必須在這個 task 執行以前完成 gulp.task('two', ['one'], function() { // 'one' 完成後 }); gulp.task('default', ['one', 'two']);

task one執行完畢後須要調用cb方法來告知task two我已經執行完成了,你能夠幹你的事了。
那在咱們實際運用中,一般是這樣的:

gulp.task('clean', function (cb) { gulp.src(['tmp']) .pipe(clean()); });

這個時候clean結束的cb要寫在哪呢?是這樣嗎?

gulp.task('clean', function (cb) { gulp.src(['tmp']) .pipe(clean()); cb(); });

對於理解什麼叫異步的人來講這種方法確定是不行的,clean還沒完成,cb已經執行了。好在!!!
好在咱們能夠利用gulp中的時間監聽來作結束判斷:

gulp.task('clean', function (cb) { gulp.src(['tmp']) .pipe(clean()), .on('end', cb); }); gulp.task('concat', [clean], function(){ gulp.src('blabla') .pipe('blabla') .dest('blabla'); });

因爲gulp是用node實現的,因此必然綁定了數據流的監聽事件,咱們經過監聽stream event end來達到這個目的。
而不得不吐槽的是經過在task後面寫[]依賴的方式也並不優雅,一般能夠經過其餘插件來達到順序執行的效果,寫法如同grunt,可是每一個task的end事件的監聽也是少不了的。
若是你的任務很少的時候,直接在回調後面執行concat也是能夠的:

gulp.task('clean', function(){}) gulp.task('concat', function(){}) gulp.task('clean-concat', ['clean'], function(){ gulp.start('concat'); })

4. 其餘要交代的

  1. gulp真的只有src, pipe, dest, watch, run這幾個API嗎? 不,因爲gulp繼承了Orchestrator(<4.0),因此具有了另一些API,包括start等。固然這些API是官方不推薦使用的。會致使代碼的複雜度提高,因此並無出如今官方文檔中。
  2. 不建議將多個操做寫在同個task中,這樣程序並不知道任務及時結束,如:
gulp.task('test', function(cb) { gulp.src('bootstrap/js/*.js') .pipe(gulp.dest('public/bootstrap')) .on('end', cb); gulp.src('jquery.cookie/jquery.cookie.js') .pipe(gulp.dest('public/jquery')) .on('end', cb); });
  1. 儘可能減小task的數量,不少任務其實能夠在一個task中用多個pipe來執行,只須要咱們在打包等時候規劃好文件夾及任務流。

對了,gulp4.0會帶給咱們不少驚喜(wtf!),雖然它仍是遲遲未發佈... 暫時不想去踩坑。讀者可自行Google。

相關文章
相關標籤/搜索