Webpack原理與實(shí)踐之Webpack究竟解決了什么問(wèn)題?
本文轉(zhuǎn)載自微信公眾號(hào)「前端萬(wàn)有引力」,作者一川。轉(zhuǎn)載本文請(qǐng)聯(lián)系前端萬(wàn)有引力公眾號(hào)。
寫(xiě)在前面
Webpack所解決的問(wèn)題是:如何在前端項(xiàng)目中更高效地管理和維護(hù)項(xiàng)目中的每個(gè)資源。想要搞明白webpack,就必須先對(duì)它想要解決的問(wèn)題或目標(biāo)有個(gè)充分的認(rèn)識(shí)。
模塊化的演化進(jìn)程
階段1:文件劃分方式
- |--01-files
- |--module-01.js
- |--module-02.js
- |--index.html
- <!-- index.html -->
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Document</title>
- </head>
- <body>
- <script src="./module-01.js"></script>
- <script src="./module-02.js"></script>
- <script>
- // 直接使用全局成員
- fun()//可能存在命名沖突
- console.log(data)
- data = "onechuan";//數(shù)據(jù)可能被修改
- </script>
- </body>
- </html>
- //module-01.js
- function fun(){
- console.log("module-01-fun");
- }
- //module-02.js
- var data = "yichuan";
在這種進(jìn)行文件劃分方式中,缺點(diǎn)有:
- 模塊直接在全局工作,大量模塊成員會(huì)污染全局作用域
- 沒(méi)有私有空間,所有模塊內(nèi)的成員都可以在模塊外部被訪問(wèn)或修改
- 一旦模塊增多,容易產(chǎn)生命名沖突
- 無(wú)法管理模塊與模塊之間的依賴關(guān)系
- 在維護(hù)過(guò)程中很難分辨每個(gè)成員所屬的模塊
當(dāng)然,當(dāng)你項(xiàng)目代碼少的時(shí)候可能感受不到這種方式的缺點(diǎn),但是當(dāng)你代碼變大的時(shí)候,這種劃分方式造成的問(wèn)題會(huì)暴露地淋漓盡致。
階段2:命名空間方式
在命名空間方式中,解決命名沖突的問(wèn)題外,其它問(wèn)題依舊存在
- <script src="./module-01.js"></script>
- <script src="./module-02.js"></script>
- <script>
- module01.fun();//module-01-fun
- module02.fun();//module-02-fun
- // 模塊成員依然可以被修改
- module01.data = "onechuan"
- console.log(module01.data);//"onechuan"
- </script>
- // module-01.js
- window.module01 = {
- fun:function(){
- console.log("module-01-fun");
- }
- }
- // module-02.js
- window.module02 = {
- data:"yichuan",
- fun:function(){
- console.log("module-02-fun")
- }
- }
階段3:立即執(zhí)行函數(shù) IIFE
通過(guò)在文件中使用立即函數(shù)和閉包的方式,可以有效解決前面的全局作用污染的問(wèn)題,而且可以通過(guò)參數(shù)明顯表明這個(gè)模塊的依賴。雖然解決了全局作用域污染問(wèn)題,但是不能夠通過(guò)代碼控制模塊的加載順序的問(wèn)題,不便于對(duì)模塊的管理,因?yàn)槟悴恢朗裁茨K沒(méi)有被導(dǎo)入,什么模塊被移除。
- <script src="./module-01.js"></script>
- <script src="./module-02.js"></script>
- <script>
- module01.fun();//module-01-fun
- module02.fun();//module-02-fun
- </script>
- // module-01.js
- ;(function(){
- var name = "module-01";
- function fun(){
- console.log(name+"-fun");
- }
- window.module01 = {
- fun:fun
- }
- })()
- // module-01.js
- ;(function(){
- var name = "module-02";
- function fun(){
- console.log(name+"-fun");
- }
- window.module02 = {
- fun:fun
- }
- })()
模塊化規(guī)范的出現(xiàn)
當(dāng)前是通過(guò)約定規(guī)則實(shí)現(xiàn)模塊化的方式,不同的開(kāi)發(fā)者在實(shí)施過(guò)程中會(huì)出現(xiàn)一些差別。
模塊化規(guī)范的兩點(diǎn)標(biāo)準(zhǔn):
- 一個(gè)統(tǒng)一的模塊化標(biāo)準(zhǔn)規(guī)范
- 一個(gè)可以自動(dòng)加載模塊的基礎(chǔ)庫(kù)
CommonJS規(guī)范
CommonJS規(guī)范是Node.js中所遵循的模塊規(guī)范,該規(guī)范約定一個(gè)文件就是一個(gè)模塊,每個(gè)模塊都有單獨(dú)的作用域,通過(guò)module.exports到處成員,再通過(guò)require函數(shù)進(jìn)行導(dǎo)入模塊。
AMD規(guī)范
雖然CommonJS規(guī)范使用也很方便,但是早期指定前端標(biāo)準(zhǔn)化模塊時(shí),并沒(méi)有直接選擇CommonJS規(guī)范,而是專(zhuān)門(mén)為瀏覽器短重新設(shè)計(jì)了AMD規(guī)范,也就是異步模塊定義規(guī)范。
同期推出了Require.js,除了實(shí)現(xiàn)AMD模塊化規(guī)范,本身也是一個(gè)非常強(qiáng)大的模塊加載器。
- //AMD規(guī)范定義了一個(gè)模塊
- //module01.js
- define(["./module02.js"],function(){
- return{
- fun:function(){
- console.log("hello-module-02");
- }
- }
- })
- //AMD規(guī)范載入了一個(gè)模塊
- //module02.js
- require(["./module01.js"],function(module01){
- module01.fun();
- })
在Javascript的標(biāo)準(zhǔn)逐漸走向完善,端模塊化規(guī)范的最佳實(shí)踐方式也基本得到了統(tǒng)一:
- 在Node.js環(huán)境中,遵循CommonJS規(guī)范來(lái)實(shí)現(xiàn)模塊化
- 在瀏覽器環(huán)境中,遵循ES Modules規(guī)范實(shí)現(xiàn)模塊化
ES Modules規(guī)范是ES2015中才定義的模塊化系統(tǒng),是最近幾年才制定的標(biāo)準(zhǔn),存在環(huán)境兼容性問(wèn)題,隨著webpack等一系列打包工具的流行,此規(guī)范才逐漸開(kāi)始被普及。經(jīng)過(guò)幾年的迭代,ES Modules規(guī)范已經(jīng)成為現(xiàn)今最主流的前端模塊化標(biāo)準(zhǔn)。
- //module01.js
- export default function fun(){
- console.log("hello-module01");
- }
- //module02.js
- import fun from "./module01.js";
- fun()
模塊打包工具的出現(xiàn)
隨著日益復(fù)雜的項(xiàng)目開(kāi)發(fā),在前端應(yīng)用開(kāi)發(fā)過(guò)程中不僅只有JS代碼需要模塊化,HTML和CSS這些資源文件也會(huì)面臨需要被模塊化的問(wèn)題。從宏觀角度看,這些文件也都應(yīng)該被看做前端應(yīng)用的一個(gè)模塊,只不過(guò)這些模塊的種類(lèi)和用途和JS不同。
我們知道ES Modules近些年才制定的模塊化標(biāo)準(zhǔn),因此在當(dāng)前瀏覽器環(huán)境可存在兼容問(wèn)題,因此需要經(jīng)過(guò)模塊打包工具將ES6代碼轉(zhuǎn)為ES5代碼。
參考文章
《Webpack官方文檔》
《webpack原理與實(shí)踐》
寫(xiě)在最后
前端模塊化的發(fā)展過(guò)程和最終統(tǒng)一的ES Modules標(biāo)準(zhǔn)使我們深入了解webpack必須掌握的基礎(chǔ)內(nèi)容,也是現(xiàn)代前端開(kāi)發(fā)者必不可少的基礎(chǔ)知識(shí)。