webpack 模塊化機(jī)制

為什么需要模塊化?

隨著網(wǎng)站內(nèi)容越來越復(fù)雜贤壁,瀏覽器和用戶的交互越來越細(xì)膩悼枢,網(wǎng)站再也不是簡單的內(nèi)容呈現(xiàn),更像是一個(gè)復(fù)雜的客戶端軟件脾拆,其中html/css/js代碼越來越多馒索,邏輯越來越復(fù)雜,越來越不便于管理名船,為了解決這個(gè)問題绰上,才出現(xiàn)了模塊化的概念,也就是說模塊化更多的是工程方面的產(chǎn)出渠驼,為了應(yīng)對更復(fù)雜的網(wǎng)站開發(fā)蜈块。

梳理下網(wǎng)站的發(fā)展過程,大家也就知道為什么模塊化是必然出現(xiàn)的了:
? 早期一個(gè)html文件迷扇,通過style引入內(nèi)聯(lián)css百揭,通過script引入內(nèi)聯(lián)js。
? 晚一點(diǎn)一個(gè)html蜓席,通過link引入外部css信峻,通過script引入外部js。
? 再復(fù)雜一點(diǎn)瓮床,多個(gè)html盹舞,各自引入自己的css和js。
? 再復(fù)雜一點(diǎn)隘庄,多個(gè)html中有復(fù)用的css和js踢步,怎么辦呢?拆分文件唄丑掺,將css和js拆分成小文件获印,然后通過link和script分別引入或者手動(dòng)合并之后引入。
? 再復(fù)雜一點(diǎn)街州,有很多js/css的小文件兼丰,手動(dòng)解決會有缺陷或者容易出錯(cuò)玻孟,怎么辦呢?引入自動(dòng)化工具唄鳍征,自動(dòng)合并黍翎,壓縮等。
? 再復(fù)雜一點(diǎn)艳丛,想要按需合并js/css小文件匣掸,開發(fā)模式自動(dòng)監(jiān)聽改動(dòng),甚至可以將圖片等其他靜態(tài)資源當(dāng)做模塊氮双。
? 再復(fù)雜一點(diǎn)碰酝,...

到現(xiàn)在為止,都有哪些優(yōu)秀的模塊化方案呢戴差?

非模塊

通過多個(gè)script標(biāo)簽引入多個(gè)js文件送爸,也即通過文件的方式來管理模塊。

<script src="module1.js"></script>
<script src="module2.js"></script>
<script src="libraryA.js"></script>
<script src="module3.js"></script>

這種原始的方案有很多顯而易見的弊端:
? 污染全局作用域暖释,多人協(xié)作簡直是災(zāi)難碱璃。
? 手動(dòng)維護(hù)script標(biāo)簽的順序,多頁應(yīng)用更慘饭入。
? 大型項(xiàng)目資源難以管理嵌器,容易留下隱患。

CommonJs谐丢,同步require

該方案的核心思想就是允許模塊通過require方案同步加載依賴的其他模塊爽航,通過exportsmodule.exports來暴露出需要的接口。

require("../moduleA.js");

exports.doStuff = function() {
    console.log('hello world');
};

這種原始的方案的優(yōu)點(diǎn):
? 復(fù)用性強(qiáng)乾忱。
? 有不少可以拿來即用的模塊讥珍,生態(tài)不錯(cuò)。
? 實(shí)現(xiàn)簡單窄瘟,使用簡單衷佃。

這種原始的方案的弊端:
? 同步加載不適合瀏覽器,瀏覽器的請求都是異步加載蹄葱。
? 不能并行加載多個(gè)模塊氏义。

AMD,異步require

該方案只有一個(gè)主要接口define(id?, dependencies?, factory)图云,他要在聲明模塊的時(shí)候指定所有的依賴dependencies惯悠,并傳入到factory中,對于依賴的模塊異步加載并執(zhí)行竣况。

// 定義
define("mymodule", ["dep1", "dep2"], function(d1, d2) {
});

// 加載
require(["module", "../file"], function(module, file) {
});

這種原始的方案的優(yōu)點(diǎn):
? 異步加載適合瀏覽器克婶。
? 可并行加載多個(gè)模塊。

這種原始的方案的弊端:
? 模塊定義方式不優(yōu)雅,不符合標(biāo)準(zhǔn)模塊化

ES6模塊

該方案最大的特點(diǎn)就是靜態(tài)化情萤,靜態(tài)化的優(yōu)勢在于可以在編譯的時(shí)候確定模塊的依賴關(guān)系以及輸入輸出的變量鸭蛙。上面提到的CommonJs和AMD都只能在運(yùn)行時(shí)確定這些東西。

import "jquery";
export function doStuff() {}

這種原始的方案的優(yōu)點(diǎn):
? 可靜態(tài)分析筋岛,提前編譯娶视。
? 面向未來的標(biāo)準(zhǔn)。

這種原始的方案的弊端:
? 瀏覽器原生兼容性差泉蝌,所以一般都編譯成ES5歇万。
? 目前可以拿來即用的模塊少揩晴,生態(tài)差勋陪。

webpack模塊化機(jī)制

webpack并不強(qiáng)制你使用某種模塊化方案,而是通過兼容所有模塊化方案讓你無痛接入項(xiàng)目硫兰,當(dāng)然這也是webpack牛逼的地方诅愚。
有了webpack,你可以隨意選擇你喜歡的模塊化方案劫映,至于怎么處理模塊之間的依賴關(guān)系及如何按需打包违孝,放輕松,webpack會幫你處理好的泳赋。

webpack的模塊化有什么特點(diǎn)雌桑?

? 可以兼容多模塊風(fēng)格,無痛遷移老項(xiàng)目祖今。
? 一切皆模塊校坑,js/css/圖片/字體都是模塊。
? 靜態(tài)解析千诬,按需打包耍目,動(dòng)態(tài)加載。

require("./style.css");
require("./style.less");
require("./template.jade");
require("./image.png");

本來徐绑,模塊化方案僅僅是針對JS邪驮。但實(shí)際項(xiàng)目中,我們希望coffeescript也能被當(dāng)做模塊傲茄,進(jìn)一步毅访,css/less/sass是不是也可以被當(dāng)做模塊,再進(jìn)一步盘榨,html/image/template是不是也可以被當(dāng)做模塊俺抽。
想到這些就覺得腦洞大開,不可思議较曼!但是webpack居然做到了磷斧!webpack提供的loaders可以對文件做預(yù)處理,從而實(shí)現(xiàn)了一切皆模塊瓦哎。

那么叭喜,webpack到底對模塊代碼做了什么知态?

非模塊化代碼

一行alert('hello world');代碼呵扛,經(jīng)過webpack打包后因痛,會生成如下50行代碼供屉;

/******/ (function(modules) { // webpackBootstrap
/******/    // The module cache
/******/    var installedModules = {};

/******/    // The require function
/******/    function __webpack_require__(moduleId) {

/******/        // Check if module is in cache
/******/        if(installedModules[moduleId])
/******/            return installedModules[moduleId].exports;

/******/        // Create a new module (and put it into the cache)
/******/        var module = installedModules[moduleId] = {
/******/            exports: {},
/******/            id: moduleId,
/******/            loaded: false
/******/        };

/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

/******/        // Flag the module as loaded
/******/        module.loaded = true;

/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }


/******/    // expose the modules object (__webpack_modules__)
/******/    __webpack_require__.m = modules;

/******/    // expose the module cache
/******/    __webpack_require__.c = installedModules;

/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "";

/******/    // Load entry module and return exports
/******/    return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports) {

    alert('hello world');

/***/ }
/******/ ]);

上面編譯出來的代碼主要包含兩個(gè)部分:Runtime昧廷,模塊蝶防。上半部分就是Runtime憔晒,作用是保證模塊順序加載和運(yùn)行藻肄。下半部分是我們的JS代碼,包裹了一個(gè)函數(shù)拒担,也就是模塊嘹屯。
運(yùn)行的時(shí)候模塊是作為Runtime的參數(shù)被傳進(jìn)去的。

(function(modules) {
    // Runtime
})([
    // 模塊數(shù)組
])

那么从撼,非模塊化代碼被編譯后會有什么缺陷呢州弟?
? 模塊不再暴露在全局作用域,模塊的全局變量也不再是全局變量低零。
? 模塊被引入的時(shí)候只是執(zhí)行代碼而無法將模塊賦值婆翔。因?yàn)榉悄K化規(guī)范的代碼沒有通過AMD的return或者CommonJsexports導(dǎo)出模塊本身。

AMD模塊

AMD的代碼

define([], function() {
    alert('hello world!');
});

經(jīng)過webpack打包掏婶,會生成如下核心代碼:

function(module, exports, __webpack_require__) {
    var __WEBPACK_AMD_DEFINE_ARRAY__, // AMD依賴列表
        __WEBPACK_AMD_DEFINE_RESULT__; // AMD factory函數(shù)的返回值啃奴,即模塊內(nèi)容

    __WEBPACK_AMD_DEFINE_ARRAY__ = [];

    // 執(zhí)行factory函數(shù),獲取返回值作為模塊內(nèi)容
    // 函數(shù)體使用.apply調(diào)用雄妥,函數(shù)體中this為exports
    // 形參則分別對應(yīng)依賴列表中的各個(gè)依賴模塊
    __WEBPACK_AMD_DEFINE_RESULT__ = function() {
        alert('hello world!');
    }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__);

    // 如果模塊內(nèi)容不為空最蕾,則通過module.exports返回
    // 如果為空,則不處理茎芭,在Runtime中聲明為{}
    if (__WEBPACK_AMD_DEFINE_RESULT__ !== undefined) {
        module.exports = __WEBPACK_AMD_DEFINE_RESULT__;
    }
}
CommonJs

CommonJs的代碼

var me = {
    sayHello:function(){
        alert('hello world!');
    }
};
module.exports = me;

經(jīng)過webpack打包揖膜,會生成如下核心代碼:

function(module, exports) {
    var me = {
        sayHello: function() {
            alert('hello world!');
        }
    };

    module.exports = me;
}

模塊化是拆分,那傳輸怎么辦梅桩?

博文開頭提到壹粟,模塊化是工程化的需求,是為了更好的管理代碼宿百,最后上線的代碼并不應(yīng)該是這樣的趁仙,假設(shè)我們用兩個(gè)極端的方式去加載代碼:
? N個(gè)模塊N個(gè)請求。
? 所有模塊打包成一個(gè)文件垦页,一個(gè)請求雀费。
顯然,這兩種都不是最優(yōu)方案痊焊,第一種請求數(shù)量過多盏袄,第二種請求文件過大忿峻。

理論上,最優(yōu)方案是:按需打包辕羽,即將該頁面需要的所有模塊打包成一個(gè)文件逛尚,保證請求最少,且請求的代碼都是需要的刁愿。

在webpack之前的構(gòu)建工具里绰寞,都實(shí)現(xiàn)不了這個(gè)“最優(yōu)方案”,因?yàn)樗鼈儾恢滥K之前的依賴關(guān)系铣口,自然就不能按需打包了滤钱。
而webpack出現(xiàn)之后,它的代碼分片功能讓webpack擁有了按需打包的特性脑题,從而鶴立雞群件缸。當(dāng)然,webpack還有很多其他優(yōu)秀的特性旭蠕。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末停团,一起剝皮案震驚了整個(gè)濱河市旷坦,隨后出現(xiàn)的幾起案子掏熬,更是在濱河造成了極大的恐慌,老刑警劉巖秒梅,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旗芬,死亡現(xiàn)場離奇詭異,居然都是意外死亡捆蜀,警方通過查閱死者的電腦和手機(jī)疮丛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辆它,“玉大人誊薄,你說我怎么就攤上這事∶誊裕” “怎么了呢蔫?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長飒筑。 經(jīng)常有香客問我片吊,道長,這世上最難降的妖魔是什么协屡? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任俏脊,我火速辦了婚禮,結(jié)果婚禮上肤晓,老公的妹妹穿的比我還像新娘爷贫。我一直安慰自己认然,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布漫萄。 她就那樣靜靜地躺著季眷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪卷胯。 梳的紋絲不亂的頭發(fā)上子刮,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機(jī)與錄音窑睁,去河邊找鬼挺峡。 笑死,一個(gè)胖子當(dāng)著我的面吹牛担钮,可吹牛的內(nèi)容都是我干的橱赠。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼箫津,長吁一口氣:“原來是場噩夢啊……” “哼狭姨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起苏遥,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤饼拍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后田炭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體师抄,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年教硫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了叨吮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瞬矩,死狀恐怖茶鉴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情景用,我是刑警寧澤涵叮,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站丛肢,受9級特大地震影響围肥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蜂怎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一穆刻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧杠步,春花似錦氢伟、人聲如沸榜轿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谬盐。三九已至,卻和暖如春诚些,著一層夾襖步出監(jiān)牢的瞬間飞傀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工诬烹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留砸烦,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓绞吁,卻偏偏與公主長得像幢痘,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子家破,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容