可能就是好奇心略重了,讀了一下webpack打包後的bundle.js的代碼,複雜的模塊可能讀不懂,但簡單的hello world模塊我仍是能看懂的。沒什麼目的,就是想經過幾個簡單的模塊,一條簡單的webpack命令,一個神奇的bundle.js代碼來了解webpack是怎麼把遵循commonJs規範的模塊應用到瀏覽器端的。webpack
幾個簡單的模塊:
web
一條簡單的webpack命令:
數組
一個神奇的bundle.js:瀏覽器
1 /******/ (function(modules) { // webpackBootstrap 2 /******/ // The module cache 3 /******/ var installedModules = {}; 4 5 /******/ // The require function 6 /******/ function __webpack_require__(moduleId) { 7 8 /******/ // Check if module is in cache 9 /******/ if(installedModules[moduleId]) 10 /******/ return installedModules[moduleId].exports; 11 12 /******/ // Create a new module (and put it into the cache) 13 /******/ var module = installedModules[moduleId] = { 14 /******/ exports: {}, 15 /******/ id: moduleId, 16 /******/ loaded: false 17 /******/ }; 18 19 /******/ // Execute the module function 20 /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 22 /******/ // Flag the module as loaded 23 /******/ module.loaded = true; 24 25 /******/ // Return the exports of the module 26 /******/ return module.exports; 27 /******/ } 28 29 30 /******/ // expose the modules object (__webpack_modules__) 31 /******/ __webpack_require__.m = modules; 32 33 /******/ // expose the module cache 34 /******/ __webpack_require__.c = installedModules; 35 36 /******/ // __webpack_public_path__ 37 /******/ __webpack_require__.p = ""; 38 39 /******/ // Load entry module and return exports 40 /******/ return __webpack_require__(0); 41 /******/ }) 42 /************************************************************************/ 43 /******/ ([ 44 /* 0 */ 45 /***/ function(module, exports, __webpack_require__) { 46 47 /* 48 打印文本的index模塊 49 */ 50 var text = __webpack_require__(1); 51 console.log(text); 52 53 /***/ }, 54 /* 1 */ 55 /***/ function(module, exports) { 56 57 /* 58 生成文本的Hello world模塊 59 */ 60 module.exports = 'Hello world!'; 61 62 /***/ } 63 /******/ ]);
註釋太多有點懵逼,但是細看也不就是一個當即執行的函數表達式嘛(IIFE),這是JavaScript中常見的獨立做用域的方法。這裏我將註釋簡化一下:緩存
1 (function(modules){ 2 //module緩存對象 3 var installedModules = {}; 4 //require函數 5 function __webpack_require__(moduleId){ 6 //檢查module是否在cache中 7 if(installedModules[moduleId]){ 8 return installedModules[moduleId].exports; 9 } 10 //若不在cache中則新建module並放入cache中 11 var module = installedModules[moduleId] = { 12 exports: {}, 13 id: moduleId, 14 loaded: false 15 }; 16 //執行module函數 17 modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 18 //標記module已經加載 19 module.loaded = true; 20 //返回module的導出模塊 21 return module.exports; 22 } 23 24 //暴露modules對象(__webpack_modules__) 25 __webpack_require__.m = modules; 26 //暴露modules緩存 27 __webpack_require__.c = installedModules; 28 //設置webpack公共路徑__webpack_public_path__ 29 __webpack_require__.p = ""; 30 //讀取入口模塊而且返回exports導出 31 return __webpack_require__(0); 32 33 })([function(module, exports, __webpack_require__){ /*模塊Id爲0*/ 34 var text = __webpack_require__(1); 35 console.log(text); 36 },function(module, exports){ /*模塊Id爲1*/ 37 module.exports = 'Hello world'; 38 }]);
這個IIFE接收一個數組做爲參數modules,數組的每一項都是一個匿名函數表明一個模塊(index.js和hello.js模塊)。但是爲何構建命令中只出現了「index.js"呢,這是由於webpack經過靜態分析index.js文件獲得語法樹,遞歸檢測index.js所依賴的模塊,以及依賴的依賴,入口模塊和全部的依賴模塊做爲數組中的項,併合併到最終的代碼中。ide
正是因爲模塊代碼被包裝成函數後,每一個模塊的運行時機變的可控,能夠決定什麼時候調用(經過 modules[moduleId].call ),並且也有了獨立的做用域,定義變量,聲明函數都不會污染全局做用域。模塊函數的參數列表中除了commonJS規範所要求的 module 與 exports 外,還有 __webpack_require__ 函數,用來替換 require ,做用是聲明對其餘模塊的依賴並得到該模塊的 exports ( return installedModules[moduleId].exports 和 return module.exports; )。並且不須要提供模塊的相對路徑或其餘形式的ID,直接傳入該模塊在modules列表中索引便可,這樣能夠省掉模塊標識符的解析過程(準確說,webpack是把require模塊的解析過程提早到了構建期),從而能夠得到更好的運行性能。
函數
程序解讀:
bundle.js經過 __webpack_require__(0); 啓動整個程序,先檢查模塊ID = 0是否在緩存對象中,若該模塊的緩存存在返回 module.exports 即模塊所暴露出來的數據,若該模塊的緩存不在則新建立module對象(該module對象做用是用來指向真實模塊)並加入到緩存對象中,此時因爲module對象和該模塊的緩存對象 installedModules[moduleId] 的exports屬性爲沒有數據,因此須要經過執行該模塊函數來返回具體require其餘模塊的數據,傳入的上下文對象是 module.exports 和 installedModules[moduleId].exports 所共同指向的一個對象。當程序執行到 var text = __webpack_require__(1); 時,又會執行 modules[1].call ,而後 module.exports = 'Hello world'; 將執行 __webpack_require__(1) 時建立的module1的exports賦值爲Hello world,並返回,此時 __webpack_require__(1) 執行完畢,text爲Hello world並打印, __webpack_require__(0) 執行完畢。這是一個遞歸的過程,若是還有更多依賴模塊的話會更明顯。
總結一下,webpack主要作了兩部分工做:
1.分析獲得全部必須模塊併合並。
2.提供讓這些模塊有序,正常的執行環境。
性能
參考:
《React全棧 Redux+Flux+webpack+Babel整合開發》ui