webpack進階教程(一)

注:本文重點不是怎樣配置webpack.config.js並實現相應的功能,而是經過對比webpack編譯前和編譯後文件,探究webpack打包後的文件是怎樣加載執行的。
本文討論commonJS模塊化方案時,webpack的打包工做。
爲了便於討論,咱們準備了一個很是簡單的例子,涉及三個文件,分別是javascript

文件a.jshtml

module.exports ={
        say:function(){
            console.log('A is saying.');
        }
    }

文件b.jsjava

module.exports ={
        say:function(){
            console.log('B is saying.');
        }
    }

文件index.jswebpack

var a = require('./a');
var b = require('./b');
a.say();
b.say();

依賴關係很是簡單,即index.js文件依賴a.jsb.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.jsb.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.jsa.jsb.js被放進了一個數組中。入口文件(index.js)都是在數組的0的位置。入口文件的依賴(a.jsb.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能有幫助。

相關文章
相關標籤/搜索