webpack2生成程式碼分析

李CHENGXI發表於2016-10-22

原文連結

打包一個模組

// webpack.config.js
module.exports = {
    entry: {
        index: "./main.js",
    },
    output: {
        path: __dirname + '/dist',
        filename: '[name].js'
    },
};複製程式碼
// main.js, entry chunk
import { chunk2, chunk3 } from './main1';
import chunk5, { C1, C2, C3 } from './main2';

var chunk1 = 1;
exports.chunk1 = chunk1;


exports.chunk4 = {
    a: 1,
    b: 2
};

console.log(C1);
console.log(chunk3);複製程式碼
// main1.js
var chunk2 = 2;
exports.chunk2 = chunk2;


var chunk3 = 3;
exports.chunk3 = chunk3;

export function f1() {
    return 'f1';
}
export function f2() {
    return 'f2';
}複製程式碼
// main2.js
export function f3() {
    return 'f3';
}

export default class C3 {
    constructor() {

    }

    f1() {
        console.log("f1")
    }

    f2() {
        console.log("f2");
    }
}

export const C1 = 'c1';
export const C2 = 'c2';複製程式碼
// result file, index.js
(function(modules) { 
    // modules在webpack1的時候是陣列,現在變成了key值是數字的物件
    // module的快取
    var installedModules = {};

    // require方法,轉義成此
    function __webpack_require__(moduleId) {

        // 若module已被快取,直接返回
        if(installedModules[moduleId])
            return installedModules[moduleId].exports;

        // 建立一個新的module,被放入快取中
        // webpack1的時候都是全稱,現在估計為了省點空間,都變成了id => i, load => l
        var module = installedModules[moduleId] = {
            i: moduleId,
            l: false,
            exports: {}
        };

        // 執行module
        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

        // 標明此module已被載入
        module.l = true;

        // module.exports通過在執行module的時候,作為引數存進去,然後會儲存module中暴露給外界的介面,
        // 如函式、變數等
        return module.exports;
    }


    // 在原始檔中,直接使用__webpack_modules__,生成檔案用__webpack_require__.m替換
    __webpack_require__.m = modules;

    // 暴露module快取
    __webpack_require__.c = installedModules;

    // identity function for calling harmory imports with the correct context
    __webpack_require__.i = function(value) { return value; };

    // 為harmory exports 定義 getter function, configurable=false表明,此屬性不能修改
    // 例如export const,由於是常量,需要用__webpack_require__.d進行定義
    __webpack_require__.d = function(exports, name, getter) {
        Object.defineProperty(exports, name, {
            configurable: false,
            enumerable: true,
            get: getter
        });
    };

    // 相容 non-harmony 模組,這些模組如果設了__esModule屬性,則被標記為non-harmony
    __webpack_require__.n = function(module) {
        var getter = module && module.__esModule ?
            function getDefault() { return module['default']; } :
            function getModuleExports() { return module; };
        __webpack_require__.d(getter, 'a', getter);
        return getter;
    };

    // Object.prototype.hasOwnProperty.call polyfill
    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };

    // 使用__webpack_public_path__,則會替換__webpack_require__.p
    __webpack_require__.p = "//localhost:8000/";

    // 載入入口模組,並返回exports
    return __webpack_require__(__webpack_require__.s = 143);
})
/************************************************************************/
({

    143: // 入口模組
    function(module, exports, __webpack_require__) {

        module.exports = __webpack_require__(64);

    },

    64: // main.js
    function(module, exports, __webpack_require__) {

        "use strict";
        /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__main1__ = __webpack_require__(72);
        /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__main2__ = __webpack_require__(73);

        var chunk1 = 1;
        exports.chunk1 = chunk1;

        exports.chunk4 = {
            a: 1,
            b: 2
        };
        // 此如由於引用了C1,而C1又是常用,它事先定義成屬性a,此處直接引用物件的屬性a
        console.log(__WEBPACK_IMPORTED_MODULE_1__main2__["a" /* C1 */]);
        console.log(__WEBPACK_IMPORTED_MODULE_0__main1__["chunk3"]);

    },

    72: // main1.js
    function(module, exports, __webpack_require__) {

        "use strict";
        /* unused harmony export f1 */
        /* unused harmony export f2 */
        // 此處註釋表示,這兩個harmony export模組沒有被使用,後續如果使用unglify外掛,f1與f2會被去掉
        // 這個就是著名的tree-shaking
        var chunk2 = 2;
        exports.chunk2 = chunk2;

        var chunk3 = 3;
        exports.chunk3 = chunk3;

        function f1() {
            return 'f1';
        }
        function f2() {
            return 'f2';
        }

    },

    73: // main2.js
    function(module, exports, __webpack_require__) {

        "use strict";
        /* unused harmony export f3 */
        /* unused harmony export default */
        /* harmony export (binding) */ __webpack_require__.d(exports, "a", function() { return C1; });
        /* unused harmony export C2 */
        function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

        function f3() {
            return 'f3';
        }

        var C3 = function () {
            function C3() {
                _classCallCheck(this, C3);
            }

            C3.prototype.f1 = function f1() {
                console.log("f1");
            };

            C3.prototype.f2 = function f2() {
                console.log("f2");
            };

            return C3;
        }();

        var C1 = 'c1';
        var C2 = 'c2';

    }

});複製程式碼

整個立即執行函式,主要是webpack_require, webpack_require.n, webpack_require.d起作用。installedModules是用於快取已經載入的模組。

非同步載入


// webpack.config.js
module.exports = {
    entry: {
        index: "./main.js",
    },
    output: {
        path: __dirname + '/dist',
        filename: '[name].js',
        chunkFilename: "js/[name].js",
    },
};複製程式碼
// main.js
var chunk1 = 1;
exports.chunk1 = chunk1;

function errorLoading(err) {
    console.error('Dynamic page loading failed', err);
}
function loadRoute(cb) {
    console.log("dynamic loading success");
    return (module) => cb(null, module.default);
}

// 符合es6規範的非同步載入模組方法
System.import('./main1')
                  .then(loadRoute(cb))
                  .catch(errorLoading);複製程式碼
// main1.js
var chunk2 = 2;
exports.chunk2 = chunk2;


var chunk3 = 3;
exports.chunk3 = chunk3;

export function f1() {
    return 'f1';
}
export function f2() {
    return 'f2';
}

export default function f3() {
    return 'f3';
}複製程式碼
// result file, index.js複製程式碼
// result file, 0.js
webpackJsonp([0],{
    144:
    function(module, exports, __webpack_require__) {

        "use strict";
        /* harmony export (immutable) */ exports["f1"] = f1;
        /* harmony export (immutable) */ exports["f2"] = f2;
        /* harmony export (immutable) */ exports["default"] = f3;
        var chunk2 = 2;
        exports.chunk2 = chunk2;

        var chunk3 = 3;
        exports.chunk3 = chunk3;

        function f1() {
            return 'f1';
        }
        function f2() {
            return 'f2';
        }

        function f3() {
            return 'f3';
        }

    }
});複製程式碼
// result file index.js
(function(modules) { // webpackBootstrap
    // install a JSONP callback for chunk loading
    var parentJsonpFunction = window["webpackJsonp"];
    // 全域性定義webpackJsonp,讓chunk載入的時候,直接可呼叫
    window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {
        // 將異載入的moreModules,新增到entry chunk的modules裡面
        // 然後使所有chunk標記為已載入,並觸發回撥函式
        var moduleId, chunkId, i = 0, resolves = [], result;
        for(;i < chunkIds.length; i++) {
            chunkId = chunkIds[i];
            if(installedChunks[chunkId]) {
                resolves.push(installedChunks[chunkId][0]);
            }
            installedChunks[chunkId] = 0;
        }
        // 將moreModules存入modules中
        for(moduleId in moreModules) {
            if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
                modules[moduleId] = moreModules[moduleId];
            }
        }

        if(parentJsonpFunction) {
            parentJsonpFunction(chunkIds, moreModules, executeModules);
        }
        // resolves就是需要觸發的回撥
        while(resolves.length) {
            resolves.shift()();
        }

    };

    // The module cache
    var installedModules = {};

    // objects to store loaded and loading chunks
    var installedChunks = {
        3: 0
    };

    // 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] = {
            i: moduleId,
            l: false,
            exports: {}
        };

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

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

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

    // 非同步載入函式,返回promise物件
    __webpack_require__.e = function requireEnsure(chunkId) {
        // 如果已經載入,則返回Promise.resolve
        if(installedChunks[chunkId] === 0)
            return Promise.resolve();

        // an Promise means "currently loading".
        if(installedChunks[chunkId]) {
            return installedChunks[chunkId][2];
        }
        // 開始載入
        var head = document.getElementsByTagName('head')[0];
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.charset = 'utf-8';
        script.async = true;
        script.timeout = 120000;

        // 載入的資源位置
        script.src = __webpack_require__.p + "js/chunk/" + ({}[chunkId]||chunkId) + ".js";
        var timeout = setTimeout(onScriptComplete, 120000);
        script.onerror = script.onload = onScriptComplete;
        function onScriptComplete() {
            // avoid mem leaks in IE.
            script.onerror = script.onload = null;
            clearTimeout(timeout);
            var chunk = installedChunks[chunkId];
            if(chunk !== 0) {
                if(chunk) chunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));
                installedChunks[chunkId] = undefined;
            }
        };
        head.appendChild(script);

        var promise = new Promise(function(resolve, reject) {
            // resolve與reject,屬於installedChunks[chunkId]的回撥函式,
            // 在webpackJsonpCallback函式中,有可能被呼叫
            installedChunks[chunkId] = [resolve, reject];
            console.log(installedChunks[chunkId]);
        });
        return installedChunks[chunkId][2] = promise;
    };

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

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

    // identity function for calling harmory imports with the correct context
    __webpack_require__.i = function(value) { return value; };

    // define getter function for harmory exports
    __webpack_require__.d = function(exports, name, getter) {
        Object.defineProperty(exports, name, {
            configurable: false,
            enumerable: true,
            get: getter
        });
    };

    // getDefaultExport function for compatibility with non-harmony modules
    __webpack_require__.n = function(module) {
        var getter = module && module.__esModule ?
            function getDefault() { return module['default']; } :
            function getModuleExports() { return module; };
        __webpack_require__.d(getter, 'a', getter);
        return getter;
    };

    // Object.prototype.hasOwnProperty.call
    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };

    // __webpack_public_path__
    __webpack_require__.p = "//localhost:8000/";

    // on error function for async loading
    __webpack_require__.oe = function(err) { console.error(err); throw err; };

    // Load entry module and return exports
    return __webpack_require__(__webpack_require__.s = 141);
})
/************************************************************************/
({

    141:
    function(module, exports, __webpack_require__) {

        module.exports = __webpack_require__(64);

    },

    64:
    function(module, exports, __webpack_require__) {

        var chunk1 = 1;
        exports.chunk1 = chunk1;

        function errorLoading(err) {
            console.error('Dynamic page loading failed', err);
        }
        function loadRoute() {
            console.log("dynamic loading success");
            return function (module) {
                console.log(module.default);
            };
        }
        // 符合es6規範的非同步載入模組
        __webpack_require__.e/* System.import */(0).then(__webpack_require__.bind(null, 144)).then(loadRoute()).catch(errorLoading);

    }

});複製程式碼

非同步載入,主要是多了webpackJsonp全域性函式,以及webpack_require.e作為載入script的函式。

CommonsChunkPlugin 提取公共包


// webpack.config.js
module.exports = {
    entry: {
        index: "./main.js",
        spa: "./spamain.js"
    },
    output: {
        path: __dirname + '/dist',
        filename: '[name].js',
        chunkFilename: "js/[name].js",
    },
    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name: "commons",
            filename: "commons.js",
            chunks: ['index', 'spa'],
        }),
    ]
};複製程式碼
// result file, index.js
(function(modules) { // webpackBootstrap
    // install a JSONP callback for chunk loading
    var parentJsonpFunction = window["webpackJsonp"];
    window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {
        // add "moreModules" to the modules object,
        // then flag all "chunkIds" as loaded and fire callback
        var moduleId, chunkId, i = 0, resolves = [], result;
        for(;i < chunkIds.length; i++) {
            chunkId = chunkIds[i];
            if(installedChunks[chunkId])
                resolves.push(installedChunks[chunkId][0]);
            installedChunks[chunkId] = 0;
        }
        for(moduleId in moreModules) {
            if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
                modules[moduleId] = moreModules[moduleId];
            }
        }
        if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);
        while(resolves.length)
            resolves.shift()();
                // 這裡比非同步載入相同的函式多了一段執行邏輯,主要用於執行entry chunk
        if(executeModules) {
            for(i=0; i < executeModules.length; i++) {
                result = __webpack_require__(__webpack_require__.s = executeModules[i]);
            }
        }
        return result;
    };

    // The module cache
    var installedModules = {};

    // objects to store loaded and loading chunks
    var installedChunks = {
        3: 0
    };

    // 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] = {
            i: moduleId,
            l: false,
            exports: {}
        };

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

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

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

    // This file contains only the entry chunk.
    // The chunk loading function for additional chunks
    __webpack_require__.e = function requireEnsure(chunkId) {
        if(installedChunks[chunkId] === 0)
            return Promise.resolve();

        // an Promise means "currently loading".
        if(installedChunks[chunkId]) {
            return installedChunks[chunkId][2];
        }
        // start chunk loading
        var head = document.getElementsByTagName('head')[0];
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.charset = 'utf-8';
        script.async = true;
        script.timeout = 120000;

        script.src = __webpack_require__.p + "js/chunk/" + ({"0":"index","1":"spa"}[chunkId]||chunkId) + ".js";
        var timeout = setTimeout(onScriptComplete, 120000);
        script.onerror = script.onload = onScriptComplete;
        function onScriptComplete() {
            // avoid mem leaks in IE.
            script.onerror = script.onload = null;
            clearTimeout(timeout);
            var chunk = installedChunks[chunkId];
            if(chunk !== 0) {
                if(chunk) chunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));
                installedChunks[chunkId] = undefined;
            }
        };
        head.appendChild(script);

        var promise = new Promise(function(resolve, reject) {
            installedChunks[chunkId] = [resolve, reject];
        });
        return installedChunks[chunkId][2] = promise;
    };

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

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

    // identity function for calling harmory imports with the correct context
    __webpack_require__.i = function(value) { return value; };

    // define getter function for harmory exports
    __webpack_require__.d = function(exports, name, getter) {
        Object.defineProperty(exports, name, {
            configurable: false,
            enumerable: true,
            get: getter
        });
    };

    // getDefaultExport function for compatibility with non-harmony modules
    __webpack_require__.n = function(module) {
        var getter = module && module.__esModule ?
            function getDefault() { return module['default']; } :
            function getModuleExports() { return module; };
        __webpack_require__.d(getter, 'a', getter);
        return getter;
    };

    // Object.prototype.hasOwnProperty.call
    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };

    // __webpack_public_path__
    __webpack_require__.p = "//localhost:8000/";

    // on error function for async loading
    __webpack_require__.oe = function(err) { console.error(err); throw err; };
})
/************************************************************************/
({

    8:
    function(module, exports, __webpack_require__) {

        "use strict";
        /* unused harmony export f1 */
        /* unused harmony export f2 */
        /* unused harmony export default */
        var chunk2 = 2;
        exports.chunk2 = chunk2;

        var chunk3 = 3;
        exports.chunk3 = chunk3;

        function f1() {
            return 'f1';
        }
        function f2() {
            return 'f2';
        }

        function f3() {
            return 'f3';
        }

    }

});複製程式碼
// main.js
webpackJsonp([0],{

    14:
    function(module, exports, __webpack_require__) {

        "use strict";
        /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__main1__ = __webpack_require__(8);
        /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__main2__ = __webpack_require__(24);

        var chunk1 = 1;
        exports.chunk1 = chunk1;

        exports.chunk4 = {
            a: 1,
            b: 2
        };

    },

    24:
    function(module, exports, __webpack_require__) {

        "use strict";
        /* unused harmony export f3 */
        /* unused harmony export default */
        /* unused harmony export C1 */
        /* unused harmony export C2 */
        /* unused harmony export C4 */
        function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

        function f3() {
            return 'f3';
        }

        var C3 = function () {
            function C3() {
                _classCallCheck(this, C3);
            }

            C3.prototype.f1 = function f1() {
                console.log("f1");
            };

            C3.prototype.f2 = function f2() {
                console.log("f2");
            };

            return C3;
        }();




        var C1 = 'c1';
        var C2 = 'c2';
        var C4 = 'c4';

    },

    41:
    function(module, exports, __webpack_require__) {

        module.exports = __webpack_require__(14);

    }

},[41]);複製程式碼
// spamain.js
webpackJsonp([1],{

16:
function(module, exports, __webpack_require__) {

    "use strict";
    /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__index_main1__ = __webpack_require__(8);

    console.log(__WEBPACK_IMPORTED_MODULE_0__index_main1__["chunk2"]);

},

43:
function(module, exports, __webpack_require__) {

    module.exports = __webpack_require__(16);

}

},[43]);複製程式碼

提取公共包的這種情況,跟非同步載入很類似,不過它將主要的功能函式都提取到common.js中,並且新增了執行module的邏輯。但主要入口的chunk都在主要邏輯的index.js與spa.js中。

webpack2使用了一些低端瀏覽器並不支援的介面,因此如果需要支援這些低端瀏覽器的業務,需要謹慎使用。

相關文章