学习参考: 来源于 可鱼不是鱼的 webpack 4 源码主流程分析(十二):打包后文件解析
学习参考: 来源于 可鱼不是鱼的 webpack 4 源码主流程分析(一):前言及总流程概览
webpack中文文档
- 调试配置:
{ // 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。 // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "webpack-demo的调试", "cwd": "${workspaceFolder}/webpack-demo", "skipFiles": ["<node_internals>/**"], "runtimeExecutable": "npm",// node 的可执行程序 "runtimeArgs": [ // 类似于命令行参数 "run-script", "debug" ], "port": 9229, // 使用什么终端工具。integratedTerminal 表示使用 Visual Studio Code 内置的终端工具,也可以配置成使用操作系统默认的终端工具。 "console": "integratedTerminal" } ] } // package.json "scripts": { "build": "webpack", "debug": "node --inspect-brk ./node_modules/.bin/webpack" },
- 代码解析— 转载: 来源于 可鱼不是鱼的 webpack 4 源码主流程分析(十二):打包后文件解析
- demo
```js //src/a.js import { add } from ‘Src/b’; import(‘./c.js’).then(m => m.sub(2, 1)); const a = 1; add(3, 2 + a);
//src/b.js import { mul } from ‘@fe_korey/test-loader?number=20!Src/e’; export function add(a, b) { return a + b + mul(10, 5); } export function addddd(a, b) { return a + b * b; }
//src/c.js import { mul } from ‘Src/d’; import(‘./b.js’).then(m => m.add(200, 100)); //require.ensure() 是 webpack 特有的,已经被 import() 取代。 export function sub(a, b) { return a - b + mul(100, 50); } //src/d.js export function mul(a, b) { const d = 10000; return a * b + d; }
//webpack.config.js var path = require(‘path’);
module.exports = { entry: { bundle: ‘./src/a.js’ }, devtool: ‘none’, output: { path: __dirname + ‘/dist’, filename: ‘[name].[chunkhash:4].js’, chunkFilename: ‘[name].[chunkhash:8].js’ }, mode: ‘development’, resolve: { alias: { Src: path.resolve(__dirname, ‘src/’) } }, module: { rules: [ { test: /.js$/, use: [ { loader: ‘babel-loader’ } ] } ] } };
+ bundle 主体结构
```js
(function(modules) {
//runtime代码
})({
'./node_modules/@fe_korey/test-loader/loader.js?number=20!./src/d.js': function(module, __webpack_exports__, __webpack_require__) {
//...模块代码d
},
'./src/a.js': function(module, __webpack_exports__, __webpack_require__) {
//...模块代码a
},
'./src/b.js': function(module, __webpack_exports__, __webpack_require__) {
//...模块代码b
}
});
-
runtime 函数主体结构
```js function(modules){ function webpackJsonpCallback(data){ //… } // 设置 script src webpack_require.p 即为 output.publicPath 配置 function jsonpScriptSrc(chunkId){ return webpack_require.p + “” + ({}[chunkId]||chunkId) + “.” + {“0”:”d680ffbe”}[chunkId] + “.js” } function webpack_require(moduleId){ //… } // 初始化 installedModules,保存所有创建过的 module,用于缓存判断 var installedModules = {};// undefined:chunk未加载, null: chunk通过prefetch/preload提前获取过 // Promise:chunk正在加载, 0:chunk加载完毕 // 数组: 结构为 [resolve Function, reject Function, Promise] 的数组, 代表 chunk 在处于加载中 var installedChunks = { bundle: 0 };
// 定义一堆挂载在__webpack_require__上的属性 //…
// jsonp 初始化 // …
// 执行入口 return webpack_require(webpack_require.s = “./src/a.js”); }
+ __webpack_require__ 属性
```js
// 异步处理
__webpack_require__.e = function requireEnsure(chunkId) {
//后文单独分析
};
// 即为传入的modules:各模块组成的对象
__webpack_require__.m = modules;
// 即为installedModules:已经缓存的对象
__webpack_require__.c = installedModules;
// 在exports对象上添加属性,即 增加导出
__webpack_require__.d = function(exports, name, getter) {
if (!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, { enumerable: true, get: getter });
}
};
// 在exports对象上添加 __esModule 属性,用于标识 es6 模块
__webpack_require__.r = function(exports) {
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(exports, '__esModule', { value: true });
};
// 创建一个伪命名空间对象
__webpack_require__.t = function(value, mode) {
//没用上,解释暂时略过
};
// 得到 getDefaultExport,即通过 __esModule 属性判断是否是 es6 来确定对应的默认导出方法
__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;
};
// 调用 hasOwnProperty,即判断对象上是否有某一属性
__webpack_require__.o = function(object, property) {
return Object.prototype.hasOwnProperty.call(object, property);
};
// 即为 publicPath,在output.publicPath配置而来
__webpack_require__.p = '';
// 错误处理
__webpack_require__.oe = function(err) {
console.error(err);
throw err;
};
- jsonp 初始化
var jsonpArray = (window['webpackJsonp'] = window['webpackJsonp'] || []); //初始化 window['webpackJsonp']对象 var oldJsonpFunction = jsonpArray.push.bind(jsonpArray); // 暂存 push 方法 jsonpArray.push = webpackJsonpCallback; //重写 jsonpArray 的 push 方法为 webpackJsonpCallback jsonpArray = jsonpArray.slice(); //拷贝 jsonpArray(不带 push 方法) for (var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]); //若入口文件加载前,chunks文件先加载了,遍历 jsonpArray 用 webpackJsonpCallback 执行 var parentJsonpFunction = oldJsonpFunction; //旧的 push 方法存入 parentJsonpFunction
-
webpack_require
```js function webpack_require(moduleId) { // 判断该module是否已经被缓存到installedModules,如果有,则直接返回它的导出exports if (installedModules[moduleId]) { return installedModules[moduleId].exports; } // 定义module并缓存 var module = (installedModules[moduleId] = { i: moduleId, l: false, exports: {} }); // 执行module代码 modules[moduleId].call(module.exports, module, module.exports, webpack_require);// 标志module已经读取完成 module.l = true;
return module.exports; }
+ 执行各同步模块代码
```js
/***/ "./src/a.js":
/*!******************!*\
!*** ./src/a.js ***!
\******************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var Src_b__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! Src/b */ "./src/b.js");
__webpack_require__.e(/*! import() */ 0).then(__webpack_require__.bind(null, /*! ./c.js */ "./src/c.js")).then(function (m) {
return m.sub(2, 1);
});
var a = 1;
Object(Src_b__WEBPACK_IMPORTED_MODULE_0__["add"])(3, 2 + a);
/***/ }),
/***/ "./src/b.js":
/*!******************!*\
!*** ./src/b.js ***!
\******************/
/*! exports provided: add, addddd */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "add", function() { return add; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "addddd", function() { return addddd; });
/* harmony import */ var _fe_korey_test_loader_number_20_Src_d__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @fe_korey/test-loader?number=20!Src/d */ "./node_modules/@fe_korey/test-loader/loader.js?number=20!./src/d.js");
function add(a, b) {
return a + b + Object(_fe_korey_test_loader_number_20_Src_d__WEBPACK_IMPORTED_MODULE_0__["mul"])(10, 5);
}
function addddd(a, b) {
return a + b * b;
}
/***/ })
- 异步加载模块 webpack_require.e
__webpack_require__.e = function requireEnsure(chunkId) { // promise队列,等待多个异步 chunk都加载完成才执行回调 var promises = []; // 先判断是否加载过该 chunk var installedChunkData = installedChunks[chunkId]; if (installedChunkData !== 0) { // 0 means "already installed". // a Promise means "currently loading". 目标 chunk 正在加载,则将 promise push到 promises 数组 if (installedChunkData) { promises.push(installedChunkData[2]); } else { // 新建一个Promise去异步加载目标chunk var promise = new Promise(function(resolve, reject) { installedChunkData = installedChunks[chunkId] = [resolve, reject]; //这里设置 installedChunks[chunkId] }); promises.push((installedChunkData[2] = promise)); // installedChunks[chunkId] = [resolve, reject, promise] var script = document.createElement('script'); var onScriptComplete; script.charset = 'utf-8'; script.timeout = 120; if (__webpack_require__.nc) { script.setAttribute('nonce', __webpack_require__.nc); } // 设置src script.src = jsonpScriptSrc(chunkId); var error = new Error(); // 设置加载完成或错误的回调 onScriptComplete = function(event) { // avoid mem leaks in IE. script.onerror = script.onload = null; clearTimeout(timeout); var chunk = installedChunks[chunkId]; if (chunk !== 0) { if (chunk) { var errorType = event && (event.type === 'load' ? 'missing' : event.type); var realSrc = event && event.target && event.target.src; error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')'; error.name = 'ChunkLoadError'; error.type = errorType; error.request = realSrc; chunk[1](error); } installedChunks[chunkId] = undefined; } }; // 设置超时处理 var timeout = setTimeout(function() { onScriptComplete({ type: 'timeout', target: script }); }, 120000); //script标签的onload事件都是在外部js文件被加载完成并执行完成后(异步不算)才被触发 script.onerror = script.onload = onScriptComplete; // script标签加入文档 document.head.appendChild(script); } } return Promise.all(promises); }
-
加载非入口文件
```js // 在模块加载后,就会立即执行的 window[‘webpackJsonp’].push() 。由 jsonp 初始化可知, 即执行 bundle 文件里的 webpackJsonpCallback 方法 (window[‘webpackJsonp’] = window[‘webpackJsonp’] || []).push([ [0], { ‘./src/c.js’: function(module, webpack_exports, webpack_require) { //模块 c },’./src/d.js’: function(module, webpack_exports, webpack_require) { //模块 d } } ]);
+ webpackJsonpCallback
```js
function webpackJsonpCallback(data) {
var chunkIds = data[0];
var moreModules = data[1]; //异步 chunk 的各模块组成的对象
var moduleId,
chunkId,
i = 0,
resolves = [];
// 这里收集 resolve 并将所有 chunkIds 标记为已加载
for (; i < chunkIds.length; i++) {
chunkId = chunkIds[i];
if (Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
resolves.push(installedChunks[chunkId][0]); //将 resolve push 到 resolves 数组中
}
installedChunks[chunkId] = 0; //标记为已加载
}
// 遍历各模块组成的对象,将每个模块都加到 modules
for (moduleId in moreModules) {
if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
modules[moduleId] = moreModules[moduleId];
}
}
// 执行保存的旧 push 方法,可能是 array.push (即 push 到 window.webpackJsonp),也可能是前一个并行执行了 runtime 的 bundle 的 webpackJsonpCallback,即递归执行 webpackJsonpCallback,如多入口同时 import 同一个 module 的情况。
if (parentJsonpFunction) parentJsonpFunction(data);
//循环触发 resolve 回调
while (resolves.length) {
resolves.shift()();
}
}
- 异步加载小结
1)通过 webpack_require 加载运行入口 module 模块代码里遇到 import()即执行 webpack_require.e 加载异步 chunk;
2)webpack_require.e 使用模拟 jsonp 的方式及创建 script 标签来加载异步 chunk,并为每个 chunk 创建一个 promise;
3)等到异步 chunk 被加载后,会执行 window[‘webpackJsonp’].push,即webpackJsonpCallback 方法;
4)webpackJsonpCallback 里将异步 chunk 里的 module 加入到 modules, 并触发前面创建 promise 的 resolve 回调,然后执行其 then 方法即 webpack_require 去加载新的 module。