注:本文重點不是怎樣配置webpack.config.js
並實現相應的功能,而是經過對比webpack編譯前和編譯後文件,探究webpack打包後的文件是怎樣加載執行的。
本文討論commonJS
模塊化方案時,webpack
的打包工做。
爲了便於討論,咱們準備了一個很是簡單的例子,涉及三個文件,分別是javascript
文件a.js
html
module.exports ={ say:function(){ console.log('A is saying.'); } }
文件b.js
java
module.exports ={ say:function(){ console.log('B is saying.'); } }
文件index.js
webpack
var a = require('./a'); var b = require('./b'); a.say(); b.say();
依賴關係很是簡單,即index.js
文件依賴a.js
和b.js
兩個文件。並且是採用commonJS方式來引用的。
個人config
文件也貼一下。git
var htmlPlugin = require('html-webpack-plugin'); module.exports = { entry:{ index:'./src/index.js' }, output:{ path:'builds', filename:'[name].js', chunkFilename:'chunk.[name].js' }, plugins:[ new htmlPlugin({ filename:__dirname+'/builds/index.html', template:'./index.html' }) ], devServer:{ contentBase:'./builds', inline:true } }
還有一個很是簡單的index.html
文件。github
<!DOCTYPE html> <html> <head> <title>commonJS測試</title> </head> <body> </body> </html>
另外,本問的示例代碼已經放到Github
上,請 點擊這裏查看。
咱們在目錄下運行一下webpack
命令,builds文件夾裏的文件,就是被webpack處理過的文件,也是咱們要討論的重點。
咱們來看看文件目錄:
web
builds
裏,是編譯後的文件,src裏,是咱們的原始文件。
那麼a.js
和b.js
呢?打包進builds/index.js
文件裏面了。打包其實就是幹這個的,把多個文件合併到一個或少數幾個文件裏。
咱們點開builds/index.js
,發現咱們的代碼被改的面目全非。你們點擊這裏看完整的代碼,下面是部分片斷。express
(function(modules){ var installedModules = {}; function __webpack_require__(moduleId) {/*省略*/} /*省略*/ return __webpack_require__(0); })([ //這部分是index.js function(module, exports, __webpack_require__){ var a = __webpack_require__(1); var b = __webpack_require__(2); a.say(); b.say(); }, //這部分是a.js function(module, exports){ module.exports ={ say:function(){ console.log('A is saying.'); } } }, //這部分是b.js function(module, exports){ module.exports ={ say:function(){ console.log('B is saying.'); } } } ])
在上面代碼中,咱們的index.js
、a.js
和b.js
被放進了一個數組中。入口文件(index.js
)都是在數組的0
的位置。入口文件的依賴(a.js
和b.js
),會根據依賴的狀況日後排。
這個數組做爲IIFE中函數的參數modules
傳入function
中。注意匿名函數中的return __webpack_require__(0);
,這句調用將執行數組中的第一個函數。數組
第一個函數:webpack-dev-server
function(module, exports, __webpack_require__) { var a = __webpack_require__(1); var b = __webpack_require__(2); a.say(); b.say(); }
__webpack_require__(1)也就是執行數組中的第二個函數。
第二個函數:
function(module, exports) { module.exports ={ say:function(){ console.log('A is saying.'); } } }
第二個函數的執行後的結果,就是把module.exports
中的內容賦給了變量a
。到這裏,你們對代碼的結構有了瞭解,可是關鍵的__webpack_require__
方法是怎樣工做的呢?咱們來分析下代碼:
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; }
這裏注意modules[moduleId].call(module.exports, module, module.exports, __webpack_require__)
這行代碼。其中的modules
就是咱們剛纔講的數組(modules
不是module
)。module.exports
是模塊的上下文,也就是this
。因此,若是咱們在index.js
里加一句this.name='jack'
,那麼最終這個等價於module.exports.name='jack'
;
此外,咱們還能夠訪問到module.id
這個屬性。這並無什麼實際的做用,只是能夠加深咱們的理解。好比,我在a.js
中:
module.exports ={ say:function(){ console.log('A is saying.'); console.log(module.id); } }
是能夠輸出該模塊的id
的。多說一句,若是你用webpack-dev-server
,它會打包進去其餘的文件,這個id
會變的比較大(我測試的是75)。有一個可能會用到的module.loaded
屬性。咱們的模塊在第一次執行的時候,module.loaded
仍是false
,執行事後才被設置爲true
。
好比下面的代碼:
index.js
if (module.loaded) { var a = require('./a'); a.say(); } else { var b = require('./b'); b.say(); }
在咱們的例子中,這個是每次執行的結果都是B is saying.
由於每次執行index.js
模塊中的代碼都是第一次執行。
在實際的開發中,有可能有要判斷當前代碼是否是第一次執行的需求。
還有一個重要的變量installedModules
。咱們加載過的模塊中的module.exports
對象,會保存在installedModules[moduleId]中。這樣下次調用,就能夠直接返回module.exports
。
OK,此次就講到這裏,但願對同窗們學習理解webpack
能有幫助。