Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webpack2生成代码分析 #14

Open
lcxfs1991 opened this issue Oct 22, 2016 · 5 comments
Open

webpack2生成代码分析 #14

lcxfs1991 opened this issue Oct 22, 2016 · 5 comments

Comments

@lcxfs1991
Copy link
Owner

lcxfs1991 commented Oct 22, 2016

原文链接

打包一个模块

// 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使用了一些低端浏览器并不支持的接口,因此如果需要支持这些低端浏览器的业务,需要谨慎使用。

@lcxfs1991 lcxfs1991 changed the title Webpack2.0生成代码分析 webpack2.0生成代码分析 Oct 22, 2016
@lcxfs1991 lcxfs1991 changed the title webpack2.0生成代码分析 webpack2生成代码分析 Oct 22, 2016
@lcxfs1991 lcxfs1991 reopened this Oct 22, 2016
@lijialiang
Copy link

webpack2正式版本了啦?

@lcxfs1991
Copy link
Owner Author

@lilijialiang beta版

@1445453337
Copy link

#17 #17

@sponia-joker
Copy link

想请问一下,webpack打包的代码中并没有调用__webpack_require__.n函数。该函数是被设计成在用户自己的模块中被调用的吗?如果是这样一般在什么场景下会用到该函数。

@huanggm
Copy link

huanggm commented Jan 21, 2018

@lcxfs1991 似乎缺少spamain.js源码?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants