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