前端構建工具發展及其比較


我們一定會感嘆前端技術發展之快,各種可以提高開發效率的新思想和框架層出不窮。但是他們都有一個共同特點:源代碼無法直接運行,必須通過轉換后才能正常運行。

本文前兩部分摘自吳浩麟所著《深入淺出Webpack》1.2,版權歸原作者所所有

構建工具就是做這件事,將源代碼轉換成可以執行的JavaScript、CSS、HTML 代碼,包括如下內容:

  • 代碼轉換:將 TypeScript 編譯成JavaScript、將 SCSS 編譯成 CSS等。
  • 文件優化:壓縮JavaScript、CSS、HTML 代碼,壓縮合並圖片等。
  • 代碼分割:提取多個頁面的公共代碼,提取首屏不需要執行部分代碼讓其異步記在。
  • 模塊合並:在采用模塊化的項目里會有很多個模塊和文件,需要通過構建功能將模塊分類合並成一個文件。
  • 自動刷新:監聽本地源代碼變化,自動重新構建、刷新瀏覽器。
  • 代碼校驗:在代碼被提交到倉庫前需要校驗代碼是否符合規范,以及單元測試是否通過。
  • 自動發布:更新代碼后,自動構建出線上發布代碼並傳輸給發布系統。

構建其實是工程化、自動化思想在前端開發中的體現,將一系列流程用代碼去實現,讓代碼自動化地執行這一系列復雜的流程。構建為前端開發注入了更大的活力,解放了我們的生產力。

歷史上先后出現了一系列構建工具,他們各有優缺點。由於前端工程師很熟悉 JavaScript,Node.js 又可以勝任所有構建需求,所以大多數構建工具都是用 Node.js 開發的,下面來一一介紹他們。

一、構建工具發展史及趨勢

Npm Scripts

 
 

Npm Scripts(NPM腳本)是一個任務執行者。NPM是安裝Node時附帶的一個包管理器,Npm Script 則是 NPM 內置的一個功能,允許在 package.json 文件里面使用 scripts 字段定義任務:

1
2
3
4
5
6
{
"scripts":{
"dev": "node dev.js",
"pub": "node build.js"
}
}

 

里面的 scripts 字段是一個對象,每個屬性對應一個 Shell 腳本,以上代碼定義了兩個任務,dev 和 pub。其底層實現原理是通過調用 Shell 去運行腳本命令,例如,執行 npm run pub 命令等同於執行 node build.js 命令。

優點:npm scripts 的優點是內置,無需安裝其他依賴
缺點:功能太簡單,雖然提供了 pre 和 post 兩個鈎子,但不能方便的管理多個任務之間的依賴。

附:npm scripts 使用指南

Grunt

 
Grunt
Grunt

Grunt 現在已基本不用了,簡單說一下。Grunt 和 Npm Scripts 類似,也是一個任務執行者。Grunt 有大量現成的插件封裝了常見任務,也能管理任務之間的依賴關系,自動化地執行依賴任務,每個任務的具體執行代碼和依賴關系寫在配置文件 gruntfile.js 里。

Grunt 的優點是:

  • 靈活,它只負責執行我們定義好的任務;
  • 大量可復用插件封裝好了常見的構建任務。

Grunt 的缺點是集成度不高,要寫很多配置后才可以用,無法做到開箱即用。

Grunt 相當於進化版的 Npm scripts,它的誕生其實是為了彌補 Npm Scripts 的不足。

Gulp

 
Gulp
Gulp

Gulp 是一個基於流的自動化構建工具。除了可以管理任務和執行任務,還支持監聽文件、讀寫文件。Gulp 被設計的非常簡單,只通過下面5個方法就可以支持幾乎所有構建場景:

  • 通過 gulp.task 注冊一個任務;
  • 通過 gulp.run 執行任務;
  • 通過 gulp.watch 監聽文件變化;
  • 通過 gulp.src 讀取文件;
  • 通過 gulp.dest 寫完文件。

Gulp 最大的特點是引入了流的概念,同時提供了一系列常用插件去處理流,流可以在插件之間傳遞,大致使用如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 引入 Gulp
var gulp = require("gulp");
// 引入插件
var jshint = require("gulp-jshint");
var sass = require("gulp-sass");
var concat = require("gulp-concat");
....
 
// 便宜SCSS任務
gulp.task( 'scss', function() {
// 讀取文件,通過管道喂給插件
gulp.src( './scss/*.scss')
// SCSS 插件將 scss 文件編譯成 css
.pipe(sass())
// 輸出文件
.pipe(guilp.dest( './css'));
});
 
// 合並壓縮 JavaScript 文件
gulp.task( 'scripts', function() {
gulp.src( './js/*.js')
.pipe(concat( 'all.js'))
.pipe(uglify())
.pipe(gulp.dest( './dest'));
});
 
// 監聽文件變化
gulp.task( 'watch', function() {
// 當 SCSS 文件被編輯時執行 SCSS 任務
gulp.watch( './scss/*.scss', ['sass']);
gulp.watch( './js/*.js', ['scripts']);
});

 

Gulp 的優點:好用又不失靈活,既可以單獨完成構建,也可以和其他工具搭配使用。

缺點:和Grunt 類似。集成度不高,要寫很多配置后才可以用,無法做到開箱即用。

可以將Gulp 看做是 Grunt 的加強版。相對於 Grunt ,Gulp 增加了文件監聽、讀寫文件、流式處理的功能。

FIS 3

 
FIS 3
FIS 3

Fis3是一個來自百度的優秀國產構建工具。相對於 Grunt、Gulp 這些只提供了基本功能的工具。Fis3集成了開發者常用的構建功能,如下所述。

  • 讀寫文件:通過 fis.match 讀文件,release 配置文件輸出路徑。
  • 資源定位:解析文件之間的依賴關系和文件位置。
  • 文件指紋:在通過 useHash 配置輸出文件時為文件 URL加上 md5 戳,來優化瀏覽器的緩存。
  • 文件編譯:通過 parser 配置文件解析器做文件轉換,例如將 ES6 編譯成 ES5。
  • 壓縮資源:通過 optimizer 配置代碼壓縮方法。
  • 圖片合並:通過 spriter 配置合並 CSS 里導入的圖片到一個文件中,來減少 HTTP 請求數。

大致使用如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 加 md5
fis.match( '*.{js,css,png}', {
useHash: true
});
// 通過fis3-parse-typescript插件可將 TypeScript 文件轉換成 JavaScript 文件
fis.match( '*.ts', {
parser: fis.plugin('typescript')
});
// 對CSS進行雪碧圖合並
fis.match( '*.css', {
// 為匹配到的文件分配屬性 useSprite
useSprite: true
});
// 壓縮 JavaScript
fis.match( '*.js', {
optimizer: fis.plugin('uglify-js')
});
// 壓縮CSS
fis.match( '*.css', {
optimizer: fis.plugin('clean-css')
});
// 壓縮圖片
fis.match( '*.png', {
optimizer: fis.plugin('png-compressor')
});

 

可以看出 Fis3 很強大,內置了許多功能,無需做太多配置就能完成大量工作。

Fis3的優點:集成了各種Web老發所需的構建功能,配置簡單,開箱即用。其缺點是目前官方已經不再更新和維護,不支持最新版本的Node。

FIS3是一種專注於Web開發的完整解決方案,如果將Grunt、Gulp比作汽車的發動機,那么FIS3則就是一輛完整的汽車。

Webpack

Webpack
Webpack 是一個打包模塊化的JavaScript的工具,在Webpack里一切文件皆模塊,通過 loader 轉換文件,通過Plugin 注入鈎子,最后輸出由多個模塊組合成的文件。Webpack 專注於構建模塊化項目。

其官網的首頁圖很形象的展示了 Webpack 的定義,如下圖:
Webpack 的定義
一切文件,如JavaScript、CSS、SCSS、圖片、模板,對於Webpack 來說都是一個個模塊,這樣的好處是能清晰地描繪各個模塊之間的依賴關系,以方便Webpack進行組合和打包,經過Webpack的處理,最終會輸出瀏覽器能使用的靜態資源。

Webpack具有很大的靈活性,能配置處理文件的方式,使用方法大致如下:

1
2
3
4
5
6
7
8
module.exports = {
// 所有模塊的入口,webpack從入口開始遞歸解析出所有依賴的模塊
entry: './app.js',
output: {
// 將入口所依賴的所有模塊打包成一個文件 bundle.js 輸出
filename: 'bundle.js'
}
}

 

Webpack的優點是:

  • 專注於處理模塊化的項目,能做到開箱即用、一步到位;
  • 可通過 Plugin 擴展,完整好用又不失靈活性;
  • 使用場景不局限於Web開發;
  • 社區龐大活躍,經常引入緊跟時代發展的新特性,能為大多數場景找到已有的開源擴展;
  • 良好的開發體驗;

Webpack的缺點是:只能用於采用模塊化開發的項目。

Rollup

 
Rollup
Rollup

Rollup 是一個和 Webpack 很類似但專注於ES6的模塊打包工具。它的亮點在於,針對ES6源碼進行 Tree Shaking,以去除那些已經被定義但沒被使用的代碼並進行 Scope Hoisting,以減少輸出文件的大小和提升運行性能。然而 Rollup 的這些亮點隨后就被 Webpack 模仿和實現了。由於 Rollup 的使用方法和 Webpakc 差不多,所以這里就不詳細介紹如何使用 Rollup 了,而是詳細說明他們的差別:

  • Rollup 是在Webpack 流行后出現的替代品;
  • Rollup 生態鏈不完善,體驗還不如Webpack;
  • Rollup 的功能不如 Webpack 完善,但其配置和使用更簡單;
  • Rollup 不支持 Code Spliting, 但好處是在打包出來的代碼中沒有 Webpack 那段模塊的加載、執行和緩存的代碼。

Rollup 在用於打包JavaScript庫時比 Webpack 更有優勢,因為其打包出來的代碼更小、更快。但他的功能不夠完善,在很多場景下都找不到現成的解決方案。

Parcel

 
parceljs
parceljs

Parcel 是 最近新起的Web 應用打包工具,適用於經驗不同的開發者。它利用多核處理提供了極快的速度,並且不需要任何配置。

Parcel的優點:

  • 極速打包。Parcel 使用 worker 進程去啟用多核編譯。同時有文件系統緩存,即使在重啟構建后也能快速再編譯。
  • 開箱即用。對 JS, CSS, HTML, 文件 及更多的支持,而且不需要插件。
  • 自動轉換。如若有需要,Babel, PostCSS, 和PostHTML甚至 node_modules 包會被用於自動轉換代碼.
  • 熱模塊替換。Parcel 無需配置,在開發環境的時候會自動在瀏覽器內隨着你的代碼更改而去更新模塊。
  • 友好的錯誤日志。當遇到錯誤時,Parcel 會輸出 語法高亮的代碼片段,幫助你定位問題。

缺點:

  • 不支持SourceMap:在開發模式下,Parcel也不會輸出SourceMap,目前只能去調試可讀性極低的代碼;
  • 不支持剔除無效代碼(TreeShaking):很多時候我們只用到了庫中的一個函數,結果Parcel把整個庫都打包了進來;
  • 一些依賴會讓Parcel出錯:當你的項目依賴了一些Npm上的模塊時,有些Npm模塊會讓Parcel運行錯誤;

Parcel需要為零配置付出代價。零配置其實是把各種常見的場景做為默認值來實現的,這雖然能節省很多工作量,快速上手,但這同時會帶來一些問題:

  • 不守規矩的node_module:有些依賴的庫在發布到Npm上時可能不小心把.babelrcpostcss.config.js tsconfig.json這些配置文件也一起發布上去了,
  • 不靈活的配置:零配置的Parcel關閉了很多配置項,在一些需要的配置的場景下無法改變。

Parcel使用場景受限。目前Parcel只能用來構建用於運行在瀏覽器中的網頁,這也是他的出發點和專注點。在軟件行業不可能存在即使用簡單又可以適應各種場景的方案,就算所謂的人工智能也許能解決這個問題,但人工智能不能保證100%的正確性。

反觀Webpack除了用於構建網頁,還可以做:

與Webpack的詳細對比請參看這里

二、為什么選擇Webpack?

上面介紹的構建工具是按照他們的誕生時間排序的,他們是時代的產物,側面反映出 Web 開發的發展趨勢,如下所述:

  • 在 Npm Scripts 和 Grunt 時代,Web 開發要做的事情變多,流程復雜,自動化思想被引入,用於簡化流程;
  • 在 Gulp 時代,開始出現一些新語言用於提高開發效率,流程處理思想的出現是為了簡化文件轉換的流程,例如將ES6轉換為ES5;
  • 在Webpack時代,由於單頁應用的流行,網頁的功能和實現代碼變的復雜、龐大,Web開發向模塊化改進。

這些構建工具都有各自的定位和專注點,它們之間既可以單獨完成任務,也可以互相搭配起來彌補各自的不足。在了解這些常見的構建工具后,我們需要根據自己的需求去判斷應該如何進行選擇和搭配它們才能更好的滿足自己的需求。

經過多年的額發展,Webpack 已經成為構建工具中的首選,這是因為:

  • 大多數團隊在開發新項目時會采用緊跟時代的技術,這些技術幾乎都會采用“模塊化+新語言+新框架”,Webpack可以為這些新項目提供一站式的解決方案;
  • Webpack有良好的生態和維護團隊,能提供良好的開發體驗並保證質量;
  • Webpack 被全世界大量的Web開發者使用和驗證,能找到各個層面所需要的教程和經驗分享。

三、Gulp與Webpack的區別

常有人拿gulp與webpack來比較,知道這兩個構建工具功能上有重疊的地方,可單用,也可一起用,但本質的區別就沒有那么清晰。

Gulp強調的是前端開發的工作流程,我們可以通過配置一系列的task,定義task處理的事務(例如文件壓縮合並、雪碧圖、啟動server、版本控制等),然后定義執行順序,來讓gulp執行這些task,從而構建項目的整個前端開發流程。 簡單說就一個Task Runner,就是用來跑一個一個任務的。

Gulp 沒發解決的是 js module 的問題,是你寫代碼時候如何組織代碼結構的問題。

Webpack是一個前端模塊化方案,更側重模塊打包,我們可以把開發中的所有資源(圖片、js文件、css文件等)都看成模塊,通過loader(加載器)和plugins(插件)對資源進行處理,打包成符合生產環境部署的前端資源。

相同點:文件合並與壓縮(css,js),sass/less預編譯,啟動server,版本控制。

不同點,雖然都是前端自動化構建工具,但看他們的定位就知道不是對等的。

    • gulp嚴格上講,模塊化不是他強調的東西,他旨在規范前端開發流程。
    • webpack更是明顯強調模塊化開發,而那些文件壓縮合並、預處理等功能,不過是他附帶的功能。

 

原文鏈接:https://blog.zhangbing.site/2018/04/23/%E5%89%8D%E7%AB%AF%E6%9E%84%E5%BB%BA%E5%B7%A5%E5%85%B7%E5%8F%91%E5%B1%95%E5%8F%8A%E5%85%B6%E6%AF%94%E8%BE%83/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM