天天看點

前端自動化建構之Gulp

前端自動化建構之Gulp

本篇文章的核心是介紹一款強大的任務流工具Gulp,之是以題目叫做“前端自動化建構之Gulp”,是因為Gulp本身是使用JS編寫的運作在Node環境的一個npm包,并且大部分開發者也都使用它來作為前端項目的自動化建構工具。不過,從本質上說,Gulp并非隻能做前端自動化建構,後端建構釋出自動化,腳本工具集自動化,測試流程自動化等都可以使用Gulp。是以,無論你是哪個技術棧的開發者,學習使用Gulp都會對你大有裨益。

這邊文章能夠帶給你:

  • Gulp工具的安裝
  • Gulp工作原理
  • 建立自動化流程中的獨立任務
  • 任務的串行與并行
  • 使用管道處理資料流
  • 監聽檔案變更
  • 簡易前端自動化建構DEMO流程

本篇文章雖然無法将Gulp所有的細節都介紹到,但總體上來說挑選了其中最核心的部分,也是最實用的部分進行了介紹,并且通過一個前端DEMO的示例,示範使用Gulp進行[環境初始化->代碼合并->代碼壓縮->啟動伺服器->監聽檔案->熱更新浏覽器]的完整自動化流程。

一.Gulp基礎

好像任何技術棧的開發者,都會遇到耗費時間且讓人痛苦不堪的流程問題。除了基礎的研發,測試,部署流程外,往往中間還需要根據需求執行額外的任務,例如代碼檢查,代碼壓縮,代碼混淆等。這些流程雖然與研發無關,但是卻是産品上線的必備過程,繁瑣的流程不僅會消耗開發者額外的精力,對于團隊來說,也很難控制所有開發者所執行的流程都一緻且完整,例如某些成員可能由于疏忽大意而忘記做代碼壓縮就直接将項目釋出上線了。是以,将流程自動化不僅可以解放人力,更是項目穩定性的保障。Gulp就是為解決這樣問題而發現的一款簡單,高效且生态完整的自動化工具。

1.安裝Gulp

Gulp是運作在Node環境中的,是以你需要先安裝Node.js,這裡不再贅述。

Gulp工具分兩部分構成,全局Gulp CLI和本地Gulp。關于他們之間的關系,後面我們再詳細介紹。首先先來安裝全局的Gulp CLI工具,在終端執行如下指令:

sudo npm install -g gulp-cli            

安裝完成後,要檢查是否安裝成功,可以鍵入如下指令:

gulp -v      

此時,你應該可以看到如下的輸出:

CLI version: 2.3.0
Local version: Unknown      

其中,第一行表示我們安裝的全局的Gulp CLI工具的版本号,第二行表示本地Gulp版本号,本地Gulp是安裝在具體的項目目錄中的,此時我們尚未安裝。下面我們可以建立一個項目目錄,例如建立名為gulp-demo檔案夾,在目錄下使用下面的指令來初始化Node子產品:

npm init      

在目前目錄下使用如下指令來安裝本地Gulp:

npm install --save-dev gulp       

安裝完成後,再次執行gulp -v,可以看到本地Gulp的版本,如下:

CLI version: 2.3.0
Local version: 4.0.2      

需要注意,你在測試時,要使用4.x及以上的本地Gulp版本。

2.Gulp的工作原理

前面,我們安裝了Gulp CLI和本地Gulp,現在是時候來介紹下Gulp的工作原理了。在使用Gulp時,我們需要編寫一個名為Gulpfile.js的檔案,這個檔案是Gulp工作的核心,其中會定義各種任務及任務流。Gulp CLI工具是啟動Gulp的入口,其通過指令來調用項目中的本地Gulp,本地Gulp會調用Gulpfile.js檔案,加載和執行定義好的任務,在Gulpfile.js檔案中,通常又需要調用本地Gulp子產品中提供的API方法,以及本地Gulp插件或自定義函數的功能進而實作任務。工作原理如下圖所示:

前端自動化建構之Gulp

3.編寫Gulpfile.js檔案

在學習程式設計語言時,第一個程式通常都是HelloWorld,Gulp的學習也不例外,我們先來測試下Gulp的HelloWorld程式。在工程目錄下建立一個命名為Gulpfile.js的檔案,在其中編寫如下代碼:

// 引入本地Gulp子產品
var gulp = require('gulp');

// 調用task方法來定義任務
gulp.task('gulp', function(done){
  console.log("Hello Gulp!");
  done();
});      

其中,task函數用來定義任務,其第1個參數為任務名,第2個參數為要執行的任務方法,這裡我們傳入了一個自定義的函數,此函數中會傳入一個回調done參數,當任務邏輯代碼結束後,要調用此回調通知Gulp此任務已經結束,否則會阻塞Gulp後續任務的執行。

在目前目錄下,執行如下終端指令:

gulp gulp      

此指令的作用是讓全局Gulp CLI工具調用本地Gulp來執行Gulpfile.js中定義的名為gulp的任務。執行後,終端輸出如下:

[09:37:18] Using gulpfile ~/Desktop/gulp-demo/gulpfile.js
[09:37:18] Starting 'gulp'...
Hello Gulp!
[09:37:18] Finished 'gulp' after 2.55 ms      

可以看到,正确輸出的Hello Gulp!,并且執行任務的開始時間和結束時間都有記錄,任務耗時也有記錄。

二.Gulp任務鍊與資料流

我們已經知道如何通過Gulp來執行獨立的任務,但更多實際場景中,自動化的流程都不是簡單獨立的任務可以完成的,我們需要将任務組成任務鍊。例如我們模拟這幾個任務:

gulp.task('clean', function(done){
  console.log("清理建構目錄");
  done();
});

gulp.task('copy', function(done){
  console.log("複制模闆檔案");
  done();
});

gulp.task('build', function(done){
  console.log("編譯代碼");
  done();
});

gulp.task('server', function(done){
  console.log("啟動開發伺服器");
  done();
});

gulp.task('watch', function(done){
  console.log("監聽檔案變化持續熱更新");
  done();
});      

上面5個任務模拟了前端自動化建構的基本流程,這些任務間有些是有依賴關系的,比如清理建構目錄任務來在複制模闆檔案和編譯代碼之前,不然可能剛編譯好的代碼就又被清掉了,啟動開發伺服器則要在複制模闆檔案和編譯代碼之後,但是複制模闆檔案和編譯代碼這兩個任務是可以并行進行的。使用Gulp任務鍊,可以非常友善的實作任務間依賴處理。

1.series和parallel

series意為串行,parallel意為并行。通過Gulp中的這兩個API,可以友善的将任務組合成串行鍊或并行鍊,任務鍊也可以作為一個獨立的任務在組合進其他的任務鍊,這樣就是Gulp任務的組合變得非常靈活。按照前面描述的需求添加一個任務鍊如下:

gulp.task('dev', gulp.series(
  'clean',
  gulp.parallel('copy', 'build'),
  'server',
  'watch'
));      

在終端中鍵入gulp dev,輸出情況如下:

[10:38:08] Using gulpfile ~/Desktop/gulp-demo/gulpfile.js
[10:38:08] Starting 'dev'...
[10:38:08] Starting 'clean'...
清理建構目錄
[10:38:08] Finished 'clean' after 915 μs
[10:38:08] Starting 'copy'...
[10:38:08] Starting 'build'...
複制模闆檔案
[10:38:08] Finished 'copy' after 349 μs
編譯代碼
[10:38:08] Finished 'build' after 423 μs
[10:38:08] Starting 'server'...
啟動開發伺服器
[10:38:08] Finished 'server' after 180 μs
[10:38:08] Starting 'watch'...
監聽檔案變化持續熱更新
[10:38:08] Finished 'watch' after 153 μs
[10:38:08] Finished 'dev' after 5.57 ms      

從輸出可以看到,任務間的串行和并行關系已經可以滿足我們的需求,使用Gulp處理任務依賴就是這麼簡單。

我們再回過頭來看一下series和parallel兩個方法,其内可以傳入不限個數個參數,參數可以是字元串,可以是函數,也可以是其他的任務鍊組合。當參數為字元串時,其會被當成任務名來調用指定任務,當參數為函數時,會執行此函數,當參數為任務鍊組合時,會執行對應的任務鍊。當參數為函數時要特别注意,必須回調通知Gulp任務完成,否則會阻塞任務鍊。如下:

gulp.task('dev', gulp.series(
  'clean',
  function(done){
    console.log('custom method');
    done();
  },
  gulp.parallel('copy', 'build'),
  'server',
  'watch'
));      

執行後,終端輸出如下:

[10:46:31] Using gulpfile ~/Desktop/gulp-demo/gulpfile.js
[10:46:31] Starting 'dev'...
[10:46:31] Starting 'clean'...
清理建構目錄
[10:46:31] Finished 'clean' after 738 μs
[10:46:31] Starting '<anonymous>'...
custom method
[10:46:31] Finished '<anonymous>' after 238 μs
[10:46:31] Starting 'copy'...
[10:46:31] Starting 'build'...
複制模闆檔案
[10:46:31] Finished 'copy' after 520 μs
編譯代碼
[10:46:31] Finished 'build' after 490 μs
[10:46:31] Starting 'server'...
啟動開發伺服器
[10:46:31] Finished 'server' after 168 μs
[10:46:31] Starting 'watch'...
監聽檔案變化持續熱更新
[10:46:31] Finished 'watch' after 117 μs
[10:46:31] Finished 'dev' after 7.16 ms      

你或許發現了,執行到自定義的函數時,終端顯示的任務名是anonymous,表示匿名的,更好的方式是我們使用具名函數,這樣友善對任務執行狀态進行回溯,如下:

gulp.task('dev', gulp.series(
  'clean',
  function customMethod(done){
    console.log('custom method');
    done();
  },
  gulp.parallel('copy', 'build'),
  'server',
  'watch'
));      

2.關于Gulp資料流

從宏觀上了解了Gulp任務的串行與并行,現在我們再來關注下更具體的問題。本地Gulp本身提供了許多強大的API,像前面用到的task,series,parallel都是API其中之一。自動化建構,本質上是對檔案的掃描和處理,是以Gulp中也封裝了與檔案操作相關的API。

首先,讀取檔案流和輸出檔案流的兩個API分别是src和dest。

src方法用來建立一個檔案資料流,可以從檔案系統讀取檔案資料到虛拟檔案對象,虛拟檔案對象是Gulp中的一個概念,其會将檔案的名稱,資訊,資料等抽象成一個虛拟的對象,之後檔案的操作都在記憶體中進行,速度很快,直到處理完成後在輸出到指定檔案。例如開發時,我們的模闆代碼通常在src目錄下,編譯打包後會将處理後的檔案放入dest目錄下,這就涉及到檔案的拷貝處理。

在工程目錄下建立一個名為src的子檔案夾,在其中建立一個簡單的HTML檔案,修改下copy任務如下:

gulp.task('copy', function(done){
  return gulp.src('src/*.html').
  pipe(gulp.dest('dest'));
});      

再次執行整個任務流,可以看到項目根目錄中自動生成了一個dest檔案夾,并且已經将HTML檔案拷貝了進去。在這個copy任務的實作函數中,我們并沒有調用done回調,這是因為gulp的API已經實作了Gulp任務接口規範,直接傳回結果即可。

src方法讀取檔案資料流時,可以指定具體的檔案或通配符,如上代碼所示,表示将src檔案夾下所有的HTML檔案選中,建立成資料流,資料流提供的主要API是pipe方法,這個方法用來連接配接各個處理資料流的節點,可以将它了解為一個管道,輸入從一端流入,處理後從另一端流出,如果需要繼續進行其他處理,還可以連接配接其他管道。這種責任鍊的設計模式,可以非常靈活的連接配接各個處理插件。以前端代碼合并壓縮處理為例,流程如下:

檔案系統---->src[虛拟檔案流]--->檔案合并子產品[合并後的虛拟檔案流]--->代碼壓縮子產品[壓縮有的虛拟檔案流]--->dest[回寫到檔案系統]--->檔案系統

三.實踐:簡易前端自動化建構流程

前面我們邏輯上模拟了前端自動化建構過程,隻是在實作上,都是用的log進行模拟,這并不十分有趣。使其,使用Gulp搭建一個簡易的前端建構自動化流程并不複雜,幾分鐘就可以搞定。

1.安裝幾個Gulp插件

Gulp有着非常豐富的生态,本質上,任意npm子產品我們都可以直接使用,除此之外,還有4000多個專門為Gulp設計的插件,可以友善的支援各種檔案處理,代碼檢查等需求。插件位址如下:

​​https://gulpjs.com/plugins/​​

本次實踐,需要使用到2個Gulp插件和2個npm子產品,分别進行檔案合并,代碼壓縮,檔案删除和浏覽器測試熱更新。在項目目錄下使用如下指令來安裝這些子產品:

npm install --save-dev gulp-concat 
npm install --save-dev gulp-uglify  
npm install --save-dev del   
npm install --save-dev browser-sync      

在Gulpfile.js中引入這些子產品:

var gulp = require('gulp');
var concat = require ('gulp-concat');
var uglify = require('gulp-uglify');
var del = require('del')
var bSync = require('browser-sync').create();      

2.實作自動化建構流程中的幾個任務

首先我們先來實作clean任務,修改代碼如下:

gulp.task('clean', function(done){
  return del(['dest'])
});      

上面代碼的作用是将dest檔案夾删除,每次初始建構,我們都将舊的目錄删掉,以免緩存的檔案對最終的軟體包造成影響。

copy任務的實作方法不變,直接将HTML檔案模闆拷貝到指定的建構目錄即可:

gulp.task('copy', function(done){
  return gulp.src('src/*.html').
  pipe(gulp.dest('dest'));
});      

編譯的過程主要涉及到CSS相關的SASS或LESS編譯,或TS,CoffeeScript的編譯等等,為了簡單起見,我們隻做JS檔案的合并和壓縮,如下:

gulp.task('build', function(done){
  return gulp.src('src/*.js')
  .pipe(concat('main.js'))
  .pipe(uglify())
  .pipe(gulp.dest('dest'))
});      

上面代碼的作用是将src檔案夾下所有的JS檔案合并到一個名為main.js的檔案中,并進行代碼壓縮,最後輸出到dest檔案夾。

實作server任務如下:

gulp.task('server', function(done){
  bSync.init({
    server:'./dest',
  });
  done();
});      

上面代碼會啟動本地伺服器,并引導chrome項目頁面。

對于監聽本地檔案的變化,Gulp中自帶對應的API,實作watch任務如下:

gulp.task('watch', function(done){
  gulp.watch(['src/*.js'],gulp.parallel('build'));
  gulp.watch(['src/*.html'],gulp.parallel('copy'));
  gulp.watch(['dest/**/*'],function(done){
    bSync.reload('index.html');
    done()
  });
  done();
});      

這裡需要注意,由于我們對JS檔案的編譯和HTML模闆的拷貝任務是分開的,是以監聽也可以分開,當發現JS檔案發生了變化時,隻進行編譯任務即可,當發現HTML檔案變化時,隻進行模闆拷貝任務即可,當建構檔案夾有變更時,重新重新整理浏覽器,進而實作開發過程中的測試熱更新。

最後,我們可以寫一些測試檔案了,在src檔案夾下建立兩個js檔案和一個html檔案,代碼分别如下:

file1.js檔案中代碼:

function mlog(msg) {
  console.log(Date(),': 自定義輸出 -',msg);
}
mlog("hello");      

file2.js檔案中代碼:

var a = 1;
var b = 2;
var c = a + b;
console.log(c);      

index.html檔案中代碼:

<html>
<header>
  <script type="text/javascript" src="./main.js"></script>
</header>
<body>
  <h1>标題11</h1>
</body>
  
</html>      

使用gulp dev來開啟自動化建構流程,你可以打開chome的控制台,檢視控制台的輸出,嘗試修改下src檔案夾中的HTML檔案和JS檔案,儲存後可以看到對應的頁面也會發生更新。

到此,我們已經實作了一個簡單的前端開發自動化建構流程,Gulp不負你所望吧!

四.結語