原文地址:https://survivejs.com/webpack/appendices/comparison/
在以前,是可以將你的腳本寫在一起。 時(shí)代已經(jīng)改變,現(xiàn)在將JavaScript代碼分開來可能是一個(gè)復(fù)雜的工作。 隨著單頁應(yīng)用程序(SPA)的興起,這個(gè)問題已經(jīng)升級(jí)。他們傾向于依靠一些有用的系統(tǒng)(來解決這個(gè)問題)。
出于這個(gè)原因,有多種策略來加載它們。您可以立即加載它們,或者考慮需要它們時(shí)加載。Webpack支持許多這樣的策略。
Node和npm的流行,給它的包管理器提供了更多的使用環(huán)境。在npm普及之前,很難使用依賴項(xiàng)。有一段時(shí)間,人們開發(fā)出了前端特定的包管理器,但npm最終贏得了勝利?,F(xiàn)在依賴管理比以前更容易了,盡管還需要克服一些挑戰(zhàn)。
任務(wù)運(yùn)行程序與打包
歷史上,已經(jīng)有很多構(gòu)建工具。 Make可能是最著名的,它仍然是一個(gè)可行的選擇。 專門的任務(wù)運(yùn)行程序,如Grunt和Gulp,是專門為JavaScript開發(fā)人員創(chuàng)建的。 通過npm提供的插件使得任務(wù)運(yùn)行程序都強(qiáng)大而且可擴(kuò)展。 甚至可以使用npm腳本作為任務(wù)運(yùn)行程序。 這很常見,特別是webpack。
任務(wù)運(yùn)行程序是高水平的偉大工具。 它們?cè)试S您以跨平臺(tái)方式執(zhí)行操作。 當(dāng)您需要將各種資源拼接在一起并生產(chǎn)時(shí),問題就會(huì)開始。 出于此原因,存在資源整合程序,如Browserify,Brunch或webpack。
有一段時(shí)間,RequireJS很受歡迎。 它的核心是提供一個(gè)異步模塊的方法并建立在此之上。 AMD的格式在后面將會(huì)有更詳細(xì)的介紹。 幸運(yùn)的是,這些標(biāo)準(zhǔn)已經(jīng)趕上了,而且RequireJS似乎是一個(gè)很好的啟發(fā)。
Make
就像1977年最初發(fā)布的那樣,Make回來了。盡管它是一個(gè)舊工具,但它仍然是相關(guān)的。 Make允許您為各種目的編寫單獨(dú)的任務(wù)。 例如,您可以有不同的任務(wù)來創(chuàng)建生產(chǎn)構(gòu)建,壓縮JavaScript或運(yùn)行測(cè)試。 您可以在許多其他工具中找到相同的方法。
盡管Make主要用于C項(xiàng)目,但它并不以任何方式與C綁定。 James Coglan詳細(xì)討論了如何使用在JavaScript中使用Mark。 看一下下面的詹姆斯帖子里介紹的壓縮代碼的方法:
Makefile
PATH := node_modules/.bin:$(PATH)
SHELL := /bin/bash
source_files := $(wildcard lib/*.coffee)
build_files := $(source_files:%.coffee=build/%.js)
app_bundle := build/app.js
spec_coffee := $(wildcard spec/*.coffee)
spec_js := $(spec_coffee:%.coffee=build/%.js)
libraries := vendor/jquery.js
.PHONY: all clean test
all: $(app_bundle)
build/%.js: %.coffee
coffee -co $(dir $@) $<
$(app_bundle): $(libraries) $(build_files)
uglifyjs -cmo $@ $^
test: $(app_bundle) $(spec_js)
phantomjs phantom.js
clean:
rm -rf build
使用Make,您可以使用Make-specific語法和終端命令為您的任務(wù)建模,使其可以與webpack集成。
RequireJS
RequireJS可能是第一個(gè)成為真正受歡迎的腳本加載程序。 它首先正確地引入了模塊化JavaScript。 其最大的吸引力是AMD。 它引入了一個(gè)定義包裝器:
define(['./MyModule.js'], function (MyModule) {
return function() {}; // 模塊入口
});
// 或者
define(['./MyModule.js'], function (MyModule) {
return {
hello: function() {...}, // 導(dǎo)出為模塊函數(shù)
};
});
順便說一下,可以在包裝器中使用require:
define(['require'], function (require) {
var MyModule = require('./MyModule.js');
return function() {...};
});
后一種方法更簡(jiǎn)潔一點(diǎn)。 但您仍然會(huì)遇到多余的代碼。 ES6等標(biāo)準(zhǔn)解決了這個(gè)問題。
注意:Jamund Ferguson撰寫了一篇關(guān)于如何從RequireJS移植到webpack的優(yōu)秀博客系列。
npm腳本作為自動(dòng)化構(gòu)建工具
即使npm CLI(命令行界面)并非主要用于作為任務(wù)運(yùn)行的程序,由于有package.json的腳本字段是之成為可能。 考慮下面的例子:
package.json
"scripts": {
"stats": "webpack --env production --json > stats.json",
"start": "webpack-dev-server --env development",
"deploy": "gh-pages -d build",
"build": "webpack --env production"
},
這些腳本可以使用npm run列出,然后使用npm run <script>執(zhí)行。 您還可以使用諸如test:watch這樣的約定命名空間。 這種方法可以使它保持跨平臺(tái)。
取代使用rm -rf,您可能更希望使用諸如rimraf等實(shí)用程序。 在這里可以調(diào)用其他自動(dòng)化構(gòu)建工具來隱藏你正在使用的具體細(xì)節(jié)。 這樣,您可以在保持界面相同的情況下使用重構(gòu)工具。
Grunt

Grunt在前端開發(fā)人員中是最受歡迎的。它的插件架構(gòu)有助于它的流行,插件本身通常是復(fù)雜的,因此,當(dāng)配置增加時(shí),很難理解到底發(fā)生了什么。
以下是Grunt文檔的示例。 在此配置中,您定義一個(gè)linting和一個(gè)觀察任務(wù)。 當(dāng)watch任務(wù)運(yùn)行時(shí),它也會(huì)觸發(fā)lint任務(wù)。 這樣,當(dāng)您運(yùn)行Grunt時(shí),您可以在編輯源代碼時(shí)在終端中實(shí)時(shí)發(fā)出警告。
Gruntfile.js
module.exports = (grunt) => {
grunt.initConfig({
lint: {
files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
options: {
globals: {
jQuery: true,
},
},
},
watch: {
files: ['<%= lint.files %>'],
tasks: ['lint'],
},
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['lint']);
};
在實(shí)踐中,您將有許多小的任務(wù)用于特定目的,例如構(gòu)建項(xiàng)目。 Grunt有用的一個(gè)重要部分是它隱藏了大量的細(xì)節(jié)。
從遠(yuǎn)來說,這可能會(huì)有問題。從Grunt的構(gòu)建過程,你很難理解它引擎工作的具體情況。
注意:grunt-webpack插件允許您在Grunt環(huán)境中使用webpack,同時(shí)將使用等級(jí)提升到Webpack。
Gulp

Gulp采取不同的方法。 您不需要依賴每個(gè)插件的配置,而是處理實(shí)際的代碼。 Gulp建立在管道概念之上。 如果你熟悉Unix,這里也是一樣的。 您需要遵循以下概念:
- 來源匹配文件。
- 對(duì)來源執(zhí)行操作的過濾器(例如,轉(zhuǎn)換為JavaScript)
- 接收模塊庫(例如,您的構(gòu)建目錄)在哪里管理構(gòu)建結(jié)果。
這是一個(gè)示例的Gulpfile,可以讓您更好地了解從項(xiàng)目的README中獲取的方法。 它被縮寫為一個(gè)接口:
Gulpfile.js
const gulp = require('gulp');
const coffee = require('gulp-coffee');
const concat = require('gulp-concat');
const uglify = require('gulp-uglify');
const sourcemaps = require('gulp-sourcemaps');
const del = require('del');
const paths = {
scripts: ['client/js/**/*.coffee', '!client/external/**/*.coffee']
};
// 并不是所有的任務(wù)都需要使用流
// 一個(gè)gulpfile是另一個(gè)節(jié)點(diǎn)程序
// 你也可以在npm上使用所有的軟件包
gulp.task(
'clean',
del.bind(null, ['build']
);
gulp.task(
'scripts',
['clean'],
() => (
// 壓縮和復(fù)制所有的JavaScript(除了供應(yīng)商腳本)
// 源代碼一路下來
gulp.src(paths.scripts)
// 管道內(nèi)
.pipe(sourcemaps.init())
.pipe(coffee())
.pipe(uglify())
.pipe(concat('all.min.js'))
.pipe(sourcemaps.write())
.pipe(gulp.dest('build/js'))
)
);
// 文件更改時(shí)重新運(yùn)行任務(wù)
gulp.task(
'watch',
gulp.watch.bind(null, paths.scripts, ['scripts'])
);
// 默認(rèn)任務(wù)(從CLI運(yùn)行`gulp`時(shí)調(diào)用)
gulp.task(
'default',
['watch', 'scripts']
);
鑒于配置是代碼,如果遇到麻煩,您總是可以將其刪除。 您可以將現(xiàn)有的節(jié)點(diǎn)包作為Gulp插件,等等。 與Grunt相比,您可以更清楚地了解發(fā)生了什么。 盡管如此,你仍然最終寫了很多模板作為閑時(shí)任務(wù)。 那就是更新的方法。
注意:webpack-stream允許您在Gulp環(huán)境中使用webpack。
注意:Fly是與Gulp類似的工具。 它依賴于ES6發(fā)生器。
Browserify

處理JavaScript模塊一直是一個(gè)問題。 js語言本身沒有模塊的概念,直到ES6。 Ergo,這個(gè)語言在90年代被用在瀏覽器環(huán)境中。 已經(jīng)提出了包括AMD在內(nèi)的各種解決方案。
Browserify是模塊問題的一個(gè)解決方案。 它可以將CommonJS模塊捆綁在一起。 您可以將其與Gulp掛鉤,您可以找到較小的轉(zhuǎn)換工具,使您可以超越基本用法。 例如,watchify提供了一個(gè)在開發(fā)空閑的工作期間為您創(chuàng)建捆綁包的文件監(jiān)視器。
Browserify生態(tài)系統(tǒng)由很多小模塊組成。 這樣,Browserify就符合Unix的理念。 Browserify比webpack更容易采用,實(shí)際上它是一個(gè)很好的替代品。
注意:Splittable是一個(gè)Browserify包裝器,允許代碼分割,支持ES6開箱即用,Tree shaking等等。
JSPM

使用JSPM與以前的工具截然不同。 它附帶了一個(gè)自己的命令行工具,用于將新的軟件包安裝到項(xiàng)目中,創(chuàng)建一個(gè)生產(chǎn)包,等等。 它支持SystemJS插件,可以將各種格式加載到項(xiàng)目中。
Brunch

與Gulp相比,Brunch在更高層次的抽象上運(yùn)作。 它使用類似于webpack的聲明方法。 以示例為例,您可以考慮從Brunch網(wǎng)站改編以下配置:
module.exports = {
files: {
javascripts: {
joinTo: {
'vendor.js': /^(?!app)/,
'app.js': /^app/,
},
},
stylesheets: {
joinTo: 'app.css',
},
},
plugins: {
babel: {
presets: ['es2015', 'react'],
},
postcss: {
processors: [require('autoprefixer')],
},
},
};
Brunch包括像brunch new, brunch watch --server, and brunch build --production。 它包含了很多創(chuàng)造性的功能,可以使用插件擴(kuò)展。
注意:Brunch有一個(gè)實(shí)驗(yàn)性的熱模塊重新加載程序。
Webpack

您可以說Webpack采用比Browserify更單一的方法。 Browserify由多個(gè)小工具組成,而Webpack提供了一個(gè)核心,它提供了很多創(chuàng)造性的功能。
Webpack核心可以使用特定的加載程序和插件進(jìn)行擴(kuò)展。 它可以控制如何解決模塊,使您可以調(diào)整您的構(gòu)建以匹配特定情況和解決無法正常運(yùn)行的軟件包。
與其他工具相比,Webpack具有初始復(fù)雜性,但通過其廣泛的功能集成可以彌補(bǔ)這一點(diǎn)。 這是一個(gè)需要耐心的高級(jí)工具。 但是一旦了解了背后的基本思路,webpack就變得很強(qiáng)大。
其他選項(xiàng)
您可以找到更多替代品,如下所列:
- pundle宣傳自己作為下一代打包工具,并特別注意其性能。
- Rollup重點(diǎn)關(guān)注打包es6的代碼。Tree shaking是其賣點(diǎn)之一。您可以使用Rollup與webpack的加載程序rollup-loader。
- AssetGraph采用完全不同的方法,建立在HTML語義之上,使其成為超鏈接分析或結(jié)構(gòu)分析的理想選擇。webpack-assetgraph-plugin將webpack和AssetGraph結(jié)合在一起。
- FuseBox是一個(gè)專注于速度的打包工具。 它采用零配置方式,旨在開箱即用。
- StealJS是一個(gè)依賴加載器,一個(gè)專注于性能和易用性的構(gòu)建工具。
- Flipbox將多個(gè)捆綁包裹在一個(gè)統(tǒng)一的界面后面。
結(jié)語
歷史上已經(jīng)有很多JavaScript的構(gòu)建工具。 每個(gè)人都試圖以自己的方式解決一個(gè)特定的問題。 這些標(biāo)準(zhǔn)已經(jīng)開始迎頭趕上,基本語義的要求也更少了。 相反,工具可以在更高層次上競(jìng)爭(zhēng),并推動(dòng)更好的用戶體驗(yàn)。 通常,您可以一起使用幾個(gè)單獨(dú)的解決方案。
總的來說:
- 自動(dòng)化構(gòu)建工具和打包工具解決不同的問題。 您可以通過兩者實(shí)現(xiàn)類似的結(jié)果,但通常最好將它們一起使用來相互補(bǔ)充。
- 較早的工具(如Make或RequireJS)仍然具有影響力,即使它們?cè)谇岸碎_發(fā)中不如以往那樣受歡迎。
- Bundinner如Browserify或webpack解決了一個(gè)重要的問題,并幫助您管理復(fù)雜的Web應(yīng)用程序。
- 一些新興技術(shù)從不同的角度解決問題。 有時(shí)候它們建立在其他工具之上,有時(shí)它們可以一起使用。