標籤:

讀懂webpack生成的26行代碼

我們在用webpack打包之後,會生成一個打包的文件,這個文件里到底做了什麼呢?

就讓我來一步步為你分解

假如我現在有一個a.js文件

// a.jsnmodule.exports = aaaaaaa;n

我還有一個b.js文件

//b.jsnvar a = require(./a.js)nconsole.log(a)n

那麼現在我把b.js作為入口文件,打包出來的結果就是下面的代碼,注意:下面的代碼是經過我簡化之後,便於你理解webpack的代碼,只有26行,原始代碼比這個要多。

var moduleArr = [n (function(module,exports){n module.exports = "aaaaa"n }),(function(module,exports,webpackRequire){n var a = webpackRequire(0);n console.log(a)n })n];n(function(modules){n var installedModules = {};n function webpackRequire(moduleId){n if(installedModules[moduleId]){n return installedModules[moduleId].exports;n }else { //為installedModules里增加一個moduleId,它指向一個對象n var module = installedModules[moduleId] = {n i: moduleId,n l:false,//是否已經load的意思n exports:{}// 這個模塊到底exports出了什麼東西,放到這n }n modules[moduleId](module,module.exports,webpackRequire)n module.l = true //表明已經load進了installedModules對象中n return module.exports;n }n }n webpackRequire(1)n})(moduleArr)n

參數

var moduleArr = [n (function(module,exports){n module.exports = "aaaaa"n }),(function(module,exports,webpackRequire){ //注意這有一個webpackRequire的參數n var a = webpackRequire(0);n console.log(a)n })n];n

最頂部,我聲明了一個數組moduleArr,這個數組裡有兩個匿名函數,每個匿名函數都需要module和exports,但其中一個函數還需要另外一個參數,webpackRequire,這個參數是什麼呢?我們看他的函數體很明顯知道這個參數是一個方法,給它傳了一個參數0。而且,你不覺得函數體內的代碼就是我上面那兩個簡單的a.js和b.js文件中的代碼嗎?

立即執行

然後,我們看打包出來的文件中有一個立即執行函數,這個函數需要一個modules的參數,這個參數是什麼呢?在這個函數的最底部,我們把moduleArr傳進去了,說明這個參數是一個數組。

然後我們看這個函數體,好多代碼,怎麼看?

跳過所有變數聲明和函數聲明欄位,只看代碼執行欄位,哪個?就是最底部那行

webpackRequire(1)

這行代碼是要執行webpackRequire函數,並給它賦予了參數1,然後我么再往回看這個函數要做什麼。

webpackRequire函數做了什麼

接著拆分

function webpackRequire(moduleId){n if(installedModules[moduleId]){n return installedModules[moduleId].exports;n }else { //為installedModules里增加一個moduleId,它指向一個對象n var module = installedModules[moduleId] = {n i: moduleId,n l:false,//是否已經load的意思n exports:{}// 這個模塊到底exports出了什麼東西,放到這n }n modules[moduleId](module,module.exports,webpackRequire)n module.l = true //表明已經load進了installedModules對象中n return module.exports;n }n

這個函數需要一個moduleId的參數,然後函數體內有一個條件判斷語句,

然後我們看到了installedModules,是什麼?再往回看,發現它是一個空對象,那它裡面肯定沒有moduleId這個key,然後看else分支做了什麼

啊···,現在它直接給installedModules添加屬性了

installedModules[moduleId] = {n i: moduleId, // 1n l:false,//我理解的是這個模塊是否已經load的意思,如果錯了通知我n exports:{}// 這個模塊到底exports出了什麼東西,放到這n }n

添加完屬性我們接著往下看

咦,modules是什麼?就是傳給整個立即執行函數的參數,我都快忘了,這才開始調用,早幹嘛去了。

modules[1]對應的是moduleArr的第二個匿名函數,這裡執行的時候,傳給了它三個參數,module就是installedModules[1],module.exports是一個空對象,webpackRequire是一個參數,竟然把它自己都傳進去了,這個叫遞歸。

然後我們執行這個匿名函數吧!

匿名函數做了什麼

我們接著看第二個匿名函數

function(module,exports,webpackRequire){ //注意這有一個webpackRequire的參數n var a = webpackRequire(0);n console.log(a)n}n

這個函數中根本就沒有用到module和module.exports,只用到了webpackRequire,我們看到它又給webpackRequire賦參0,還記得我們第一次調用webpackRequire這個函數是什麼時候嗎?不記得就往回翻翻。

然後做了什麼?再往回看 webpackRequire函數做了什麼這一段,重複一遍。

重複完了嗎,重複完了我們就應該進入到了另外一個匿名函數

匿名函數到底做了什麼

function(module,exports){n module.exports = "aaaaa"n}n

上面那個匿名函數帶我們兜了一圈,又把我們帶到了另外一個匿名函數,注意,此時上面那個匿名函數還沒有執行完。

然後我們看當前這個匿名函數,這個函數體內有module,有exports,那麼傳進來的參數,module,module.exports就可以派上用場了,具體執行完就應該是這樣

installedModules[0] = {n i:0,n l:false,n exports: "aaaaa"n}n

好了,匿名函數就幹了這麼點事,然後我們重新回到webpackRequire函數,盡未完之事。

module.l = true //表明已經load進了installedModules對象中nreturn module.exports; // "aaaaa"n

這個return是幹嘛用的,還記得我說 有一個匿名函數並沒有執行完嗎?

function(module,exports,webpackRequire){ //注意這有一個webpackRequire的參數n var a = webpackRequire(0);n console.log(a)n}n

明白了吧,return就是把 「aaaaa」 賦值給了a,然後再控制台列印出a。

這就完了嗎?

沒呢!

module.l = true //表明已經load進了installedModules對象中nreturn module.exports; // 還是空對象{}n

那這個return出來的空對象又是誰的結果?

還記得我問第一次調用webpackRequire函數是什麼時候嗎?

嘿嘿···


推薦閱讀:

webpack 打包JS 的運行原理
淺析 Webpack 插件化設計
Webpack工程化解決方案easywebpack
你的Tree-Shaking並沒什麼卵用
Webpack 之 Loader 的使用

TAG:webpack |