模块处理
-
默认支持 commonJs 规范
-
处理 esModule 模块 导出
-
_webpack_require_.r 标记模块为 esModule
-
_webpack_require_.d 通过将 export 导出的对象添加到 module.exports 对象上,并提供getter属性
// 源码 esModule export default "name" export const age = 18; // 编译成 commonJs __webpack_require__.r(__webpack_exports__);//标记为esModule __webpack_exports__['default'] = ("name") __webpack_require__.d(__webpack_exports__, "age", function(){ return age; }) const age = 18;
-
-
处理 esModule 模块 导入
- _webpack_require_.r 标记模块为 esModule
- _webpack_require_ 处理模块导入,获取到 module.exports 的值
- _webpack_require_.n 获取模块的 default 导出(兼容 esModule/commonJs 两种方法);并通过 .a 获取
// esModule import name,{age} from './login.js' console.log(name,age); // 编译成 commonJs __webpack_require__.r(__webpack_exports__);//标记为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, _login_js_WEBPACK_IMPORTED_MODULE_0__['age']);
打包结果
// 打包后的文件是一个 IIFE,函数传入一个参数modules
(function(modules){
// 1. 缓存已经被加载过的模块
var installedModules = {};
// 2. 返回模块的exports,用于模块的加载 (require、import的实现)
function __webpack_require__(moduleId) {
if(installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
var module = installedModules[moduleId] = {
i: moduleId,
l: false,// 是否被加载
exports: {}
};
// ⚠️执行模块定义中的函数,传入参数
// 可见 webpack 默认支持 commonJS 规范
// 在模块内部的导出代码,实现对 module.exports 的赋值
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
module.l = true; // 标记该模块已被加载
return module.exports;// 最终返回模块导出内容
}
__webpack_require__.m = modules;
__webpack_require__.c = installedModules;
__webpack_require__.p = "/"; // publicPath配置将写入这里
__webpack_require__.r =function(){ // 标记模块为 esModule 属性
Object.defineProperty(exports, '__esModule', { value: true });
}
__webpack_require__.o = // 判断是否包含属性 Object.prototype.hasOwnProperty.call
__webpack_require__.d = function(exports, name, getter){ // 给模块导出对象添加属性,并提供getter
if(!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, { enumerable: true, get: getter });
}
}
// 加载指定moduleId,并将该module的导出内容进一步处理后返回
// 用来兼容处理 commonJS 和 esModule两种加载方式
__webpack_require__.t = function(value/*moduleId*/,mode/*数值*/){
if(mode & 1) value = __webpack_require__(value);
if(mode & 8) return value; // commonJS规范,无需处理直接返回
if(mode & 4) && typeof value ==="object" && value && value.__esModule) return value;
// 将模块 包装成 esModule
var ns = Object.create(null);
__webpack_require__.r(ns);// 标记为 esModule,并将模块导出内容赋值到 default 属性
Object.defineProperty(ns, "default", { enumerable: true, value:value });
// 如果 value 是一个对象,则进行解构赋值(遵从 esModule 的 export default )
if(mode & 2 && typeof value!=="string") for(var key in value) __webpack_require__.d(ns,key,function(key){ return value[key]}.bind(null,key) /*修改函数this*/);
return ns;
}
__webpack_require__.n = function(module_exports){ // 获取模块的默认导出,兼容 commonJs 和 esModule
var getter = module_exports && module_exports.__esModule ?
function getDefault(){ return module_exports['default']} :
function getModuleExports(){ return module_exports };
__webpack_require__.d(getter, 'a', getter);// 通过.a来访问导出内容
return getter;
}
// 3. 加载入口文件
return __webpack_require__(__webpack_require__.s = "./src/index.js");
})({ // 模块定义:键值对
"./src/index.js":
(function(module, exports, /* 可见exports为 module.exports的引用*/__webpack_require__){
// 源码中的 require 被替换成了 __webpack_require__
let name = __webpack_require__("./src/login.js")
console.log(name);
}),
"./src/login.js":
(function(module,exports){
// 加载模块的内容包裹在函数中,利用该函数的参数实现模块加载操作
module.exports = "login模块"
})
})
懒加载原理
- _webpack_require_.e :动态生成script标签异步加载chunk JS文件,返回 Promise 对象
- 加载chunkJS文件会触发
window["webpackJsonp"].push
执行,即触发webpackJsonpCallback
回调,合并异步模块定义,同时改变Promise 对象为resolve状态 - 在 _webpack_require_.e 的 then回调中 通过__webpack_require__.t获取模块导出内容
/*****main.js*****/
// ===> 编译前
import('./login.js').then(name=>console.log(name))
// ===> 编译后
__webpack_require__.e("login")
.then(/*⚠️传入resolve函数*/__webpack_require__.t.bind(null,"./src/login.js",7))
.then(name=>console.log(name));
/*****login.js*****/
// ===> 编译前
module.exports = "login";
// ===> 编译后
// window["webpackJsonp"].push([chunkID,模块定义])
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["login"],{
"./src/login.js":
(function(module,exports){
module.exports = "login"
})
}])
懒加载打包结果
/** 初始化部分 **/
(function(modules){
// 1. 定义 chunk异步加载的 jsonp函数,在异步模块文件被加载时调用,通过 window["webpackJsonp"].push 触发
// 该函数的作用是,改变异步模块加载__webpack_require__.e函数的promise状态,完成异步模块定义合并
// 最终在 __webpack_require__.e 的 then 回调可以成功 通过 __webpack_require__.t 加载到该异步模块内容
function webpackJsonpCallback(data){
var chunkIds = data[0];
var moreModules = data[1];
var moduleId, chunkId,i=0,resolves = [];
for(;i<chunkIds.length;i++){
chunkId = chunkIds[i];
// 该chunkId已经被加载
// installedChunks 定义????
if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]){
resolves.push(installedChunks[chunkId][0]);
}
installedChunks[chunkId] = 0;// 赋值为0,标记该chunkId已被加载
}
// ⚠️合并懒加载的模块定义
for(moduleId in moreModules){
modules[moduleId] = moreModules[moduleId];
}
// webpackJsonpCallback 重写了 window["webpackJsonp"]数组的push方法
// 以上代码均为 在数组push的操作上 代理的逻辑
if(parentJsonpFunction) parentJsonpFunction(data);
while(resolves.length){
resolves.shift()();// 执行resolve函数,代表该 chunkId 的懒加载模块已经加载完毕,可执行后续的导入操作
}
}
// 2. 定义 jsonp 函数
var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"]||[];
var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
jsonpArray.push = webpackJsonpCallback;
// 切断 jsonpArray 和 window["webpackJsonp"] 的联系
jsonpArray = jsonpArray.slice();
for(var i = 0;i<jsonpArray.length; i++;) webpackJsonpCallback(jsonArray[i]);//执行push操作
var parentJsonpFunction = oldJsonpFunction;// 在 webpackJsonpCallback 该函数
var installedChunks = {
main:0 //默认 main入口模块已被加载
}
// 3. 定义 __webpack_require__.e
__webpack_require__.e = function (chunkId){
var promises = [];
var installedChunkData = installedChunks[chunkId];
if(installedChunkData !==0){// 0 means "已经加载过了"
if(installedChunkData){
promises.push(installedChunkData[2]);
}else{
var promise = new Promise(function(resolve,reject){
installedChunkData = installedChunks[chunkId] = [resolve, reject];
})
promises.push(installedChunkData[2] = promise);
//则 installedChunks[chunkId] = [resolve, reject, promise]
// 创建 script 标签加载异步模块,执行异步模块代码
// 通过执行 window["webpackJsonp"].push 触发 webpackJsonpCallback 执行
// 从而执行 resolve 触发promise的成功回调
var script = document.createElement('script');
script.charset='utf-8';
script.timeout = 120;
script.src = jsonpScriptSrc(chunkId);
document.head.appendChild(script);
}
}
return Promise.all(promises);
}
function jsonpScriptSrc(chunkId){
return __webpack_require__.p + "" + chunkId + ".build.js";
}
}(/**模块定义**/))