總結(jié)包括:
1 了解前端打包方案的黑暗歷史
2 模塊化發(fā)展
3 Webpack3 打包流程
在說webpack之前, 我想先談一下前端打包方案這幾年的演進(jìn)歷程, 在什么場景下, 我們遇到了什么問題, 催生出了應(yīng)對這些問題的工具. 了解了需求和目的之后, 你就知道什么時(shí)候webpack可以幫到你吊输。
參考 http://javascript.ruanyifeng.com/introduction/history.html
1990年底沟蔑,歐洲核能研究組織科學(xué)家發(fā)明了萬維網(wǎng)(World Wide Web)霜大,從此可以在網(wǎng)上瀏覽網(wǎng)頁文件。
1992年底,美國國家超級電腦應(yīng)用中心(NCSA)開發(fā)人類歷史上第一個(gè)瀏覽器蠢络,從此網(wǎng)頁可以在圖形界面的窗口瀏覽。
1994年12月迟蜜,Navigator發(fā)布了1.0版刹孔,市場份額一舉超過90%。公司很快發(fā)現(xiàn)娜睛,瀏覽器需要一種可以嵌入網(wǎng)頁的腳本語言髓霞,用來控制瀏覽器行為卦睹。當(dāng)時(shí),網(wǎng)速很慢而且上網(wǎng)費(fèi)很貴酸茴,有些操作不宜在服務(wù)器端完成分预。比如兢交,如果用戶忘記填寫“用戶名”薪捍,就點(diǎn)了“發(fā)送”按鈕,到服務(wù)器再發(fā)現(xiàn)這一點(diǎn)就有點(diǎn)太晚了配喳,最好能在用戶發(fā)出數(shù)據(jù)之前酪穿,就告訴用戶“請?zhí)顚懹脩裘薄_@就需要在網(wǎng)頁中嵌入小程序晴裹,讓瀏覽器檢查每一欄是否都填寫了被济。
管理層對這種瀏覽器腳本語言的設(shè)想是:功能不需要太強(qiáng),語法較為簡單涧团,容易學(xué)習(xí)和部署只磷。那一年,正逢Java語言問世泌绣,市場推廣活動(dòng)非常成功钮追。Netscape公司決定與Sun公司合作,瀏覽器支持嵌入Java小程序阿迈。但是后來元媚,還是決定不使用Java,因?yàn)榫W(wǎng)頁小程序不需要Java這么“重”的語法苗沧。
1995年5月刊棕,Brendan Eich只用了10天,就設(shè)計(jì)完成了這種語言的第一版待逞。它是一個(gè)大雜燴甥角,語法有多個(gè)來源:
基本語法:借鑒C語言和Java語言。
數(shù)據(jù)結(jié)構(gòu):借鑒Java語言识樱,包括將值分成原始值和對象兩大類嗤无。
函數(shù)的用法:借鑒Scheme語言和Awk語言,將函數(shù)當(dāng)作第一等公民牺荠,并引入閉包翁巍。
原型繼承模型:借鑒Self語言(Smalltalk的一種變種)。
正則表達(dá)式:借鑒Perl語言休雌。
字符串和數(shù)組處理:借鑒Python語言灶壶。
為了保持簡單,這種腳本語言缺少一些關(guān)鍵的功能杈曲,比如塊級作用域驰凛、模塊胸懈、子類型(subtyping)等,但可以利用現(xiàn)有功能找出解決辦法恰响。這直接導(dǎo)致了后來JavaScript的一個(gè)特點(diǎn):對于其他語言趣钱,你需要學(xué)習(xí)語言的各種功能,而對于JavaScript胚宦,你常常需要學(xué)習(xí)各種解決問題的模式首有。而且由于來源多樣,從一開始就注定枢劝,JavaScript的編程風(fēng)格是函數(shù)式編程和面向?qū)ο缶幊痰囊环N混合體井联。
1997年7月,ECMAScript 1.0發(fā)布您旁。
1998年6月烙常,ECMAScript 2.0版發(fā)布。
1999年12月鹤盒,ECMAScript 3.0版發(fā)布蚕脏,成為JavaScript的通行標(biāo)準(zhǔn),得到了廣泛支持侦锯。
2007年10月驼鞭,ECMAScript 4.0版草案發(fā)布,對3.0版做了大幅升級率触,預(yù)計(jì)次年8月發(fā)布正式版本葱蝗。草案發(fā)布后皂甘,由于4.0版的目標(biāo)過于激進(jìn)偿枕,各方對于是否通過這個(gè)標(biāo)準(zhǔn)渐夸,發(fā)生了嚴(yán)重分歧。以Yahoo苫幢、Microsoft韩肝、Google為首的大公司哀峻,反對JavaScript的大幅升級,主張小幅改動(dòng)漾峡;以JavaScript創(chuàng)造者Brendan Eich為首的Mozilla公司,則堅(jiān)持當(dāng)前的草案且预。
2009年12月烙无,ECMAScript 5.0版正式發(fā)布涮拗。
2015年6月三热,ECMAScript 6正式發(fā)布。
實(shí)際上 javascript可以說是一個(gè)非常好用和十分優(yōu)美的語言
并且學(xué)習(xí)成本相對其它語言低的多
我認(rèn)為它的整體設(shè)計(jì) 其實(shí)是非常成功的
想像一下一堆人圍著超市的手推車 抱怨它不好用的場景吧
有人說 為什么里面沒有設(shè)計(jì)格子
有人說 為什么沒有設(shè)計(jì)剎車
有人說 為什么沒有設(shè)計(jì)檔位
......
手推車就是手推車 它已經(jīng)完美的完成了它的工作
2006年,jQuery函數(shù)庫為操作網(wǎng)頁DOM結(jié)構(gòu)提供了非常強(qiáng)大易用的接口首妖,成為了使用最廣泛的函數(shù)庫有缆,并且讓JavaScript語言的應(yīng)用難度大大降低妒貌,推動(dòng)了這種語言的流行通危。
2009年灌曙,Node.js項(xiàng)目誕生,創(chuàng)始人為Ryan Dahl在刺,它標(biāo)志著JavaScript可以用于服務(wù)器端編程,從此網(wǎng)站的前端和后端可以使用同一種語言開發(fā)蚣驼。并且魄幕,Node.js可以承受很大的并發(fā)流量,使得開發(fā)某些互聯(lián)網(wǎng)大規(guī)模的實(shí)時(shí)應(yīng)用變得容易纯陨。
2010年翼抠,三個(gè)重要的項(xiàng)目誕生阴颖,分別是NPM量愧、BackboneJS和RequireJS偎肃,標(biāo)志著JavaScript進(jìn)入模塊化開發(fā)的時(shí)代损晤。
2012年软棺,單頁面應(yīng)用程序框架(single-page app framework)開始崛起,AngularJS項(xiàng)目和Ember項(xiàng)目都發(fā)布了1.0版本尤勋。
2013年5月喘落,F(xiàn)acebook發(fā)布UI框架庫React,引入了新的JSX語法最冰,使得UI層可以用組件開發(fā)瘦棋。
2015年3月,F(xiàn)acebook公司發(fā)布了React Native項(xiàng)目暖哨,將React框架移植到了手機(jī)端赌朋,可以用來開發(fā)手機(jī)App。它會(huì)將JavaScript代碼轉(zhuǎn)為iOS平臺的Objective-C代碼,或者Android平臺的Java代碼沛慢,從而為JavaScript語言開發(fā)高性能的原生App打開了一條道路赡若。
2015年4月,Angular框架宣布团甲,2.0版將基于微軟公司的TypeScript語言開發(fā)逾冬,這等于為JavaScript語言引入了強(qiáng)類型。
2017年11月躺苦,所有主流瀏覽器全部支持 WebAssembly身腻,這意味著任何語言都可以編譯成 JavaScript,在瀏覽器運(yùn)行匹厘。
1995年到2005年嘀趟,前端是不存在打包這個(gè)說法的。那時(shí)候頁面基本是靜態(tài)的或者是服務(wù)端輸出的愈诚,JavaScript 代碼量不是很多, 直接放在 <script> 標(biāo)簽里或者弄個(gè)js文件引一下就行她按。隨后人們嘗試在一個(gè)頁面做更多事情,比如容器的顯示隱藏和切換扰路,用CSS寫彈層和圖片輪播尤溜,用隱藏的iframe和flash等作為和服務(wù)器通信的橋梁等,但由于iframe和flash技術(shù)過于復(fù)雜, 并沒能得到廣泛的推廣汗唱。
早在1999年微軟發(fā)布IE5就引入了新功能:允許JavaScript腳本向服務(wù)器發(fā)起HTTP請求。這個(gè)功能當(dāng)時(shí)并沒有引起注意丈攒,直到2004年谷歌郵箱和2005年谷歌地圖發(fā)布哩罪,才引起廣泛重視。
2005年2月巡验,AJAX這個(gè)詞第一次正式提出际插,指圍繞這個(gè)功能進(jìn)行開發(fā)的一整套做法。概括起來显设,就是一句話框弛,AJAX通過原生的 XMLHttpRequest 對象發(fā)出HTTP請求,得到服務(wù)器返回的數(shù)據(jù)后捕捂,再進(jìn)行處理瑟枫。它幾乎成了新一代網(wǎng)站的標(biāo)準(zhǔn)做法允趟,促成了Web 2.0時(shí)代的來臨捅僵。從此,AJAX成為腳本發(fā)起HTTP通信的代名詞芹枷,W3C也在2006年發(fā)布了它的國際標(biāo)準(zhǔn)允悦。
從此, 我們的頁面開始玩出各種花來了, 前端一下子出現(xiàn)了各種各樣的庫:
Prototype JavaScript framework
Dojo Toolkit
MooTools
Sencha Ext JS
jQuery……
我們開始往頁面里插入各種庫和插件, js文件也就爆炸了膝擂。因?yàn)槲募絹碓蕉嘣絹碓酱蟮蔷W(wǎng)速很慢,所以就需要各種壓縮合并工具。執(zhí)行壓縮工具最簡單的辦法就是windows上搞個(gè)bat腳本, mac/linux上搞個(gè)bash腳本, 哪幾個(gè)文件要合并在一塊的, 哪幾個(gè)要壓縮的, 發(fā)布時(shí)運(yùn)行一下腳本, 生成壓縮后的文件架馋。
基于合并壓縮技術(shù), 項(xiàng)目越做越大, 問題也越來越多: 比如使用多個(gè)庫的時(shí)候可能產(chǎn)生命名沖突狞山;庫和插件如果還依賴其他的庫和插件, 就要告知使用人, 需要先引哪些依賴庫, 那些依賴庫也有自己的依賴庫的話, 就要先引依賴庫的依賴庫, 以此類推..……
參考 模塊的寫法http://www.ruanyifeng.com/blog/2012/10/javascript_module.html
隨著網(wǎng)站逐漸變成"互聯(lián)網(wǎng)應(yīng)用程序",嵌入網(wǎng)頁的Javascript代碼越來越龐大叉寂,越來越復(fù)雜萍启。網(wǎng)頁越來越像桌面程序,需要一個(gè)團(tuán)隊(duì)分工協(xié)作办绝、進(jìn)度管理伊约、單元測試等等......開發(fā)者不得不使用軟件工程的方法,管理網(wǎng)頁的業(yè)務(wù)邏輯孕蝉。
Javascript模塊化編程屡律,已經(jīng)成為一個(gè)迫切的需求。理想情況下降淮,開發(fā)者只需要實(shí)現(xiàn)核心的業(yè)務(wù)邏輯超埋,其他都可以加載別人已經(jīng)寫好的模塊。
但是佳鳖,Javascript不是一種模塊化編程語言霍殴,它不支持"類"(class),更遑論"模塊"(module)了系吩。(ES6正式支持"類"和"模塊"来庭,但還需要很長時(shí)間才能投入實(shí)用。)
1 原始寫法
function m1(){ }
function m2(){ }
函數(shù)m1()和m2()穿挨,組成一個(gè)模塊月弛。使用的時(shí)候,直接調(diào)用就行了科盛。這種做法的缺點(diǎn)很明顯:"污染"了全局變量帽衙,無法保證不與其他模塊發(fā)生變量名沖突,而且模塊成員之間看不出直接關(guān)系贞绵。
2 對象寫法
為了解決上面的缺點(diǎn)厉萝,可以把模塊寫成一個(gè)對象,所有的模塊成員都放到這個(gè)對象里面榨崩。
var module1 = new Object({
_count : 0,
m1 : function (){ },
m2 : function (){ }
});
使用的時(shí)候谴垫,就調(diào)用這個(gè)對象的屬性。
module1.m1();
但是蜡饵,這樣的寫法會(huì)暴露所有模塊成員弹渔,內(nèi)部狀態(tài)可以被外部改寫。比如溯祸,外部代碼可以直接改變內(nèi)部計(jì)數(shù)器的值肢专。
module1._count = 5;
3舞肆、立即執(zhí)行函數(shù)寫法
使用"立即執(zhí)行函數(shù)"(Immediately-Invoked Function Expression,IIFE)博杖,可以達(dá)到不暴露私有成員的目的椿胯。
var module1 = (function(){
var _count = 0;
var m1 = function(){ };
var m2 = function(){ };
return {
m1 : m1,
m2 : m2
};
})();
獨(dú)立性是模塊的重要特點(diǎn),模塊內(nèi)部最好不與程序的其他部分直接交互剃根。為了在模塊內(nèi)部調(diào)用全局變量哩盲,必須顯式地將其他變量輸入模塊。
上面的module1模塊需要使用jQuery庫和YUI庫狈醉,就把這兩個(gè)庫(其實(shí)是兩個(gè)模塊)當(dāng)作參數(shù)輸入module1廉油。這樣做除了保證模塊的獨(dú)立性,還使得模塊之間的依賴關(guān)系變得明顯苗傅。
這方面更多的討論抒线,參見Ben Cherry的著名文章《JavaScript Module Pattern: In-Depth》。http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html
接下來將討論如何在瀏覽器環(huán)境組織不同的模塊渣慕、管理模塊之間的依賴性嘶炭。
有了模塊,我們就可以更方便地使用別人的代碼逊桦,想要什么功能眨猎,就加載什么模塊。但是强经,這樣做有一個(gè)前提睡陪,那就是大家必須以同樣的方式編寫模塊。通行的Javascript模塊規(guī)范共有兩種:CommonJS和AMD匿情。主要介紹AMD宝穗,但是要先從CommonJS 講起。
2009年码秉,美國程序員Ryan Dahl創(chuàng)造了node.js項(xiàng)目,將javascript語言用于服務(wù)器端編程鸡号。node.js的模塊系統(tǒng)转砖,就是參照CommonJS規(guī)范實(shí)現(xiàn)的。在CommonJS中鲸伴,有一個(gè)全局性方法require( )府蔗,用于加載模塊。假定有一個(gè)數(shù)學(xué)模塊math.js汞窗,就可以像下面這樣加載姓赤。
var math = require('math');
有了服務(wù)器端模塊以后,很自然地仲吏,大家就想要客戶端模塊不铆。而且最好兩者能夠兼容蝌焚,一個(gè)模塊不用修改,在服務(wù)器和瀏覽器都可以運(yùn)行誓斥。
但是CommonJS在瀏覽器內(nèi)并不適用只洒。因?yàn)閞equire()的返回是同步的, 意味著有多個(gè)依賴的話需要一個(gè)一個(gè)依次下載, 堵塞了js腳本的執(zhí)行. 所以人們就在CommonJS的基礎(chǔ)上定義了Asynchronous Module Definition (AMD)規(guī)范(2011年), 使用了異步回調(diào)的語法來并行下載多個(gè)依賴項(xiàng)。
AMD 意思是異步模塊定義劳坑。它采用異步方式加載模塊毕谴,模塊的加載不影響它后面語句的運(yùn)行。所有依賴這個(gè)模塊的語句距芬,都定義在一個(gè)回調(diào)函數(shù)中涝开,等到加載完成之后,這個(gè)回調(diào)函數(shù)才會(huì)運(yùn)行框仔。AMD也采用require( )語句加載模塊舀武,但是不同于CommonJS,它要求兩個(gè)參數(shù):
require([module], callback);
第一個(gè)參數(shù)[module]存和,是一個(gè)數(shù)組奕剃,里面的成員就是要加載的模塊;第二個(gè)參數(shù)callback捐腿,則是加載成功之后的回調(diào)函數(shù)纵朋。
require(['math'], function (math) {
math.add(2, 3);
});
require.js的誕生,解決兩個(gè)問題:
(1)實(shí)現(xiàn)js文件的異步加載茄袖,避免網(wǎng)頁失去響應(yīng)操软;
(2)管理模塊之間的依賴性,便于代碼的編寫和維護(hù)宪祥。
require.js加載的模塊聂薪,采用AMD規(guī)范。也就是說蝗羊,模塊必須按照AMD的規(guī)定來寫藏澳。具體來說,就是模塊必須采用特定的define()函數(shù)來定義耀找。如果一個(gè)模塊不依賴其他模塊翔悠,那么可以直接定義在define()函數(shù)之中。
define()和require()的區(qū)別是, define()必須要在回調(diào)函數(shù)中返回一個(gè)值作為導(dǎo)出的東西, require()不需要導(dǎo)出東西, 因此回調(diào)函數(shù)中不需要返回值, 也無法作為被依賴項(xiàng)被其他文件導(dǎo)入, 因此一般用于入口文件野芒。
比如頁面中這樣加載a.js:
<script src="js/require.js" data-main="js/a"></script>
以上是AMD規(guī)范的基本用法, 更詳細(xì)的就不多說了(反正也淘汰了~), 有興趣的可以看官方文檔蓄愁。
js模塊化問題基本解決了, css和html也沒閑著. Less,sass,stylus的css預(yù)處理器橫空出世, 說能幫我們簡化css的寫法, 自動(dòng)給你加vendor prefix。html在這期間也出現(xiàn)了一堆模板語言, 什么handlebars,ejs,jade, 可以把a(bǔ)jax拿到的數(shù)據(jù)插入到模板中, 然后用innerHTML顯示到頁面上狞悲。
托AMD和CSS預(yù)處理和模板語言的福, 我們的編譯腳本也洋洋灑灑寫了百來行. 命令行腳本有個(gè)不好的地方, 就是windows和mac/linux是不通用的, 如果有跨平臺需求的話, windows要裝個(gè)可以執(zhí)行bash腳本的命令行工具, 比如msys(目前最新的是msys2), 或者使用php或python等其他語言的腳本來編寫, 對于非全棧型的前端程序員來說, 寫bash/php/python還是很生澀的.撮抓。因此我們需要一個(gè)簡單的打包工具, 可以利用各種編譯工具, 編譯/壓縮js, css, html, 圖片等資源。
然后2012年Grunt產(chǎn)生了, 配置文件格式是我們最愛的js, 寫法也很簡單, 社區(qū)有非常多的插件支持各種編譯, lint, 測試工具摇锋。 一年多后另一個(gè)打包工具gulp誕生了, 擴(kuò)展性更強(qiáng), 采用流式處理效率更高丹拯。
依托AMD模塊化編程, SPA(Single-page application)的實(shí)現(xiàn)方式更為簡單清晰, 一個(gè)網(wǎng)頁不再是傳統(tǒng)的類似word文檔的頁面, 而是一個(gè)完整的應(yīng)用程序. SPA應(yīng)用有一個(gè)總的入口頁面, 我們通常把它命名為index.html, app.html, main.html, 這個(gè)html的<body>一般是空的, 或者只有總的布局(layout), 比如下圖:
布局會(huì)把header, nav, footer的內(nèi)容填上, 但main區(qū)域是個(gè)空的容器站超。這個(gè)作為入口的html最主要的工作是加載啟動(dòng)SPA的js文件, 然后由js驅(qū)動(dòng), 根據(jù)當(dāng)前瀏覽器地址進(jìn)行路由分發(fā), 加載對應(yīng)的AMD模塊, 然后該AMD模塊執(zhí)行, 渲染對應(yīng)的html到頁面指定的容器內(nèi)(比如圖中的main)。在點(diǎn)擊鏈接等交互時(shí), 頁面不會(huì)跳轉(zhuǎn), 而是由js路由加載對應(yīng)的AMD模塊, 然后該AMD模塊渲染對應(yīng)的html到容器內(nèi)咽笼。
雖然AMD模塊讓SPA更容易地實(shí)現(xiàn), 但小問題還是很多的:
1 不是所有的第三方庫都是AMD規(guī)范的, 這時(shí)候要配置shim, 很麻煩顷编。
2 html里面的<img>的路徑是個(gè)問題, 需要使用絕對路徑并且保持打包后的圖片路徑和打包前的路徑不變, 或者使用html模板語言把src寫成變量, 在運(yùn)行時(shí)生成。
3 不支持動(dòng)態(tài)加載css, 變通的方法是把所有的css文件合并壓縮成一個(gè)文件, 在入口的html頁面一次性加載剑刑。
4 SPA項(xiàng)目越做越大, 一個(gè)應(yīng)用打包后的js文件到了幾MB的大小. 雖然r.js支持分模塊打包, 但配置很麻煩, 因?yàn)槟K之間會(huì)互相依賴, 在配置的時(shí)候需要exclude那些通用的依賴項(xiàng), 而依賴項(xiàng)要在文件里一個(gè)個(gè)檢查媳纬。
5 所有的第三方庫都要自己一個(gè)個(gè)的下載, 解壓, 放到某個(gè)目錄下, 更別提更新有多麻煩了。 雖然可以用npm包管理工具, 但npm的包都是CommonJS規(guī)范的, 給后端Node.js用的, 只有部分支持AMD規(guī)范施掏。
6 AMD規(guī)范定義和引用模塊的語法太麻煩, 上面介紹的AMD語法僅是最簡單通用的語法, API文檔里面還有很多變異的寫法, 特別是當(dāng)發(fā)生循環(huán)引用的時(shí)候(a依賴b, b依賴a), 需要使用其他的語法解決這個(gè)問題钮惠。
7 項(xiàng)目的文件結(jié)構(gòu)不合理, 因?yàn)間runt/gulp是按照文件格式批量處理的, 所以一般會(huì)把js, html, css, 圖片分別放在不同的目錄下, 所以同一個(gè)模塊的文件會(huì)散落在不同的目錄下, 開發(fā)的時(shí)候找文件是個(gè)麻煩的事情。 code review時(shí)想知道一個(gè)文件是哪個(gè)模塊的也很麻煩, 解決辦法比如又要在imgs目錄下建立按模塊命名的文件夾, 里面再放圖片七芭。
到了這里, 我們的主角webpack登場了(2012年)(此處應(yīng)有掌聲)
參考 https://zhuanlan.zhihu.com/p/27046322
http://www.ruanyifeng.com/blog/2012/10/javascript_module.html
本質(zhì)上素挽,webpack 是一個(gè)現(xiàn)代 JavaScript 應(yīng)用程序的靜態(tài)模塊打包器(module bundler)。當(dāng) webpack 處理應(yīng)用程序時(shí)狸驳,它會(huì)遞歸地構(gòu)建一個(gè)依賴關(guān)系圖(dependency graph)预明,其中包含應(yīng)用程序需要的每個(gè)模塊,然后將所有這些模塊打包成一個(gè)或多個(gè) bundle耙箍。
安裝
npm install --save-dev webpack
npm install --save-dev webpack@<version>
webpack 通過運(yùn)行一個(gè)或多個(gè) npm scripts撰糠,會(huì)在本地 node_modules
目錄中查找安裝的 webpack:
"scripts": {
"start": "webpack --config webpack.config.js"
}
“源”代碼是用于書寫和編輯的代碼”缋ィ“分發(fā)”代碼是構(gòu)建過程產(chǎn)生的代碼最小化和優(yōu)化后的“輸出”目錄阅酪,最終將在瀏覽器中加載。
ES2015 中的 import
和 export
語句已經(jīng)被標(biāo)準(zhǔn)化汁针。雖然大多數(shù)瀏覽器還無法支持它們术辐,但是 webpack 卻能夠提供開箱即用般的支持。
大多數(shù)項(xiàng)目會(huì)需要很復(fù)雜的設(shè)置施无,這就是為什么 webpack 要支持配置文件辉词。這比在終端(terminal)中輸入大量命令要高效的多,所以讓我們創(chuàng)建一個(gè)取代以上使用 CLI 選項(xiàng)方式的配置文件webpack.config.js猾骡。
考慮到用 CLI 這種方式來運(yùn)行本地的 webpack 不是特別方便较屿,我們可以設(shè)置一個(gè)快捷方式。在 package.json添加一個(gè) npm 腳本(npm script):
{
...
"scripts": {
"build": "webpack"
},
...
}
webpack 最出色的功能之一就是卓练,除了 JavaScript,還可以通過 loader 引入任何其他類型的文件购啄。也就是說襟企,以上列出的那些 JavaScript 的優(yōu)點(diǎn)(例如顯式依賴),同樣可以用來構(gòu)建網(wǎng)站或 web 應(yīng)用程序中的所有非 JavaScript 內(nèi)容狮含。
npm install --save-dev style-loader css-loader file-loader
csv-loader xml-loader
如果我們更改了我們的一個(gè)入口起點(diǎn)的名稱顽悼,甚至添加了一個(gè)新的名稱曼振,會(huì)發(fā)生什么?生成的包將被重命名在一個(gè)構(gòu)建中蔚龙,但是我們的index.html
文件仍然會(huì)引用舊的名字冰评。我們用 HtmlWebpackPlugin
來解決這個(gè)問題。
npm install --save-dev html-webpack-plugin
通常木羹,在每次構(gòu)建前清理 /dist
文件夾甲雅,是比較推薦的做法,因此只會(huì)生成用到的文件坑填。讓我們完成這個(gè)需求抛人。
clean-webpack-plugin
是一個(gè)比較普及的管理插件。
為了更容易地追蹤錯(cuò)誤和警告脐瑰,JavaScript 提供了 source map 功能妖枚,將編譯后的代碼映射回原始源代碼。如果一個(gè)錯(cuò)誤來自于 b.js
苍在,source map 就會(huì)明確的告訴你绝页。
webpack 中有幾個(gè)不同的選項(xiàng),可以幫助你在代碼發(fā)生變化后自動(dòng)編譯代碼:
webpack's Watch Mode
webpack-dev-server
webpack-dev-middleware
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"watch": "webpack --progress --watch",
"start": "webpack-dev-server --open",
"build": "webpack"
},
開發(fā)環(huán)境(development)和生產(chǎn)環(huán)境(production)的構(gòu)建目標(biāo)差異很大寂恬。在開發(fā)環(huán)境中续誉,我們需要具有強(qiáng)大的、具有實(shí)時(shí)重新加載(live reloading)或熱模塊替換(hot module replacement)能力的 source map 和 localhost server掠剑。而在生產(chǎn)環(huán)境中屈芜,我們的目標(biāo)則轉(zhuǎn)向于關(guān)注更小的 bundle,更輕量的 source map朴译,以及更優(yōu)化的資源井佑,以改善加載時(shí)間。由于要遵循邏輯分離眠寿,我們通常建議為每個(gè)環(huán)境編寫彼此獨(dú)立的 webpack 配置躬翁。
UglifyJSPlugin
是代碼壓縮方面比較好的選擇,但是還有一些其他可選擇項(xiàng)盯拱。
import 'jQuery'
import 'bootstrap/dist/css/bootstrap.min.css'
import './css/common.css'
import img from './img/top_left.png'
import moment from 'moment'
window.moment = moment
"scripts": {
"start": "webpack-dev-server --open --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
},
const path = require('path');
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin")
const CleanWebpackPlugin = require("clean-webpack-plugin")
module.exports = {
entry: {
app: './src/index.js',
},
devtool: 'inline-source-map',
devServer: {
contentBase: "./dist"
},
plugins: [
new CleanWebpackPlugin(["dist"]),
new HtmlWebpackPlugin({
title: "Output Management",
template: './public/index.html'
}),
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery"
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
}
]
}
};