手写webpack打包源码

167 阅读2分钟
(function(modules){
    // 缓存已加载的module
    var installedModules = {};

    var __webpack_require__  = function(moduleId){
        // 该模块已被加载
        if (installedModules[moduleId]){
            return installedModules[moduleId].exports;
        }
        // 该模块未被加载,进行初始化
        var module = installedModules[moduleId] = {
            i: moduleId,
            exports:{},
            l:false // l 标记是否已被加载
        }
        // 根据模块定义,执行模块定义函数
        // 此处可见webpack 默认支持 commonJs
        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
        module.l  = true;
        
        return module.exports;
    }
    __webpack_require__.m = modules;
    __webpack_require__.r = function (exports){
        Object.defineProperty(exports,"__esModule",{value:true})
    }
    __webpack_require__.o = function (exports, name){
        return Object.prototype.hasOwnProperty.call(exports, name);
    }

    __webpack_require__.d = function (exports, name, getter){
        if (!__webpack_require__.o(exports, name)){
            Object.defineProperty(exports, name, {
                enumerable: true,
                get: getter
            })
        }
    }
    // 如果是 esModule 获取 default 属性 
    // 如果是 commonJs 获取 exports 属性
    __webpack_require__.n = function(exports){
        var getter = exports && exports.__esModule ? 
            function getDefault() { return exports['default']} :
            function getExport() { return exports };
        __webpack_require__.d(getter, 'a', getter);
        return getter;
    }

    __webpack_require__.t = function(value/**moduleId */,mode){
        if (mode & 1) value = __webpack_require__(value);
        // commonJs
        if (mode & 8) return value;
        // esModule
        if ((mode & 4) && typeof value ==='object' && value && value.__esModule) return value;
        var ns = Object.create(null);
        __webpack_require__.r(ns);
        Object.defineProperty(ns, 'default', { enumerable:true, value: value});
        // esModule的解构赋值
        if((mode & 2) && typeof value !=="string"){
            for(var key in value){
                __webpack_require__.d(ns, key, function(key){ return value[key]}.bind(null, key))
            }
        }
        return ns;
    }
    /**
     * 异步加载相关源码
     */
    // 缓存已加载的chunks,默认已加载 main chunks
    var installedChunks = {
        main: 0 // 0 代表已加载
    }
    function jsonpScriptSrc(chunkId){
        // __webpack_require__.p 为 publicPath 的值
        return __webpack_require__.p + "" + chunkId +".build.js";
    }
    // __webpack_require__.e 通过动态创建script 加载指定 chunkId 的异步模块文件
    // 返回一个 Promise 对象,代表异步文件的加载状态
    // Promise 状态由文件加载后执行 webpackJsonpCallback 改变成 resolved
    // 确保 在加载成功的回调中 可通过 __webpack_require__.t 加载到指定模块
    __webpack_require__.e = function(chunkId){
        var promises = [];
        var installedChunkData = installedChunks[chunkId];

        // 该chunkId未被加载
        if (installedChunkData!==0){
            // installedChunkData 存在但还没被加载,则获取 该chunkId 的 promise
            if (installedChunkData && installedChunkData[2]){
                promises.push(installedChunkData[2])
            }else{
                // installedChunkData 不存在,则创建
                var promise = new Promise(function (resolve, reject) {
                    installedChunkData = installedChunks[chunkId] = [resolve, reject]
                })
                promises.push(installedChunkData[2] = promise);
                /**
                 * 创建 script 标签加载 chunk 文件
                 * chunk文件被webpack编译成 window['webpackJsonp'].push 的形式添加模块定义
                 * 触发 webpackJsonpCallback 回调,改变 chunk文件加载的 Promise 状态为 resolved,即完成异步模块定义的合并
                 * 最后确保 __webpack_require__.t 可以正确加载模块
                 */
                var script = document.createElement('script');
                script.charset = 'utf-8';
                script.timeout = 120;
                script.src = jsonpScriptSrc(chunkId);// jsonpScriptSrc函数拼接chunk文件链接
                document.head.appendChild(script);
            }
        }

        return Promise.all(promises);
    }

    // jsonp回调函数,在异步文件被加载时执行,获取到chunkId 及 对应的模块定义,进行合并
    var webpackJsonpCallback = function(data){  
        var chunkIds = data[0];
        var moreModules = data[1];
        var resolves = [];

        for (var i = 0; i < chunkIds.length; i++ ){
            var chunkId = chunkIds[i];
            // 获取该chunkId 对应的 resolve 函数,改变Promise状态
            if (installedChunks[chunkId]){
                resolves.push(installedChunks[chunkId][0])
            }
            installedChunks[chunkId] = 0;// 赋值为0,标记该chunk已被加载
        }
        // 核心目的:合并异步模块定义
        for (var module in moreModules){
            modules[module] = moreModules[module];
        }
        
        while (resolves.length) {
            resolves.shift()()
        }

        if (parentJsonpFunction) parentJsonpFunction(data);
    }

    // 定义 异步模块中 执行的jsonp函数
    var jsonpArray = window['webpackJsonp'] = window['webpackJsonp'] || [];
    var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
    jsonpArray.push = webpackJsonpCallback; // 即 window['webpackJsonp'].push = webpackJsonpCallback
    jsonpArray = jsonpArray.slice();
    for (var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i])
    var parentJsonpFunction = oldJsonpFunction; // 在 webpackJsonpCallback 中调用,即webpackJsonpCallback重写了 数组push方法的

     
    return __webpack_require__(__webpack_require__.s = "./src/index.js")
    
}({
    "./src/index.js": function(module,exports,__webpack_require__){
        /**
         * import name, {age} from './login.js';
         * console.log(name, age);
         * 
         */
        __webpack_require__.r(exports);// 标记该module为esModule
        var _login_js_WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__('./src/login.js');

        var _login_js_WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(_login_js_WEBPACK_IMPORTED_MODULE_0__);
        console.log(
            _login_js_WEBPACK_IMPORTED_MODULE_0___default.a, // 通过 .a 来获取模块默认导出内容
            _login_js_WEBPACK_IMPORTED_MODULE_0__['age'])

    },
    "./src/login.js": function (module, exports, __webpack_require__){
        /**
         * export default "zce";
         * export const age = 18;
         */
        __webpack_require__.r(exports);// 标记该module为esModule
        exports['default'] = 'zce';
        __webpack_require__.d(exports, 'age', function(){return age});
        const age = 18;
    },
    // 异步加载模块
    "./src/entry.js": function (module, exports, __webpack_require__) {
        /**
         * import('./login.js').then(name=>console.log(name))
         */
        __webpack_require__.r(exports);// 标记该module为esModule
        __webpack_require__.e("login")
            .then(__webpack_require__.t.bind(null,"./src/login.js",7))
            .then(name=>console.log(name))
    }
}))