分析webpack打包后的代码
写在前⾯的
1. 版本说明
使⽤的 webpack 版本 3.0.0。
2. webpack 的核⼼思想
⼀切皆“模块(module)”
按需加载
⼀、开始
1. 准备
当前只创建⼀个⽂件 index.js,该⽂件作为⼊⼝⽂件,代码如下。
console.log('hello, world');
接着使⽤ webpack 来进⾏打包,执⾏的命令如下。
webpack index.js index.bundle.js
2. 分析
打包⽂件⽣成的⼀⼤堆代码,实际上就是⼀个⾃执⾏函数,仅传⼊⼀个参数为 modules,且该对象为⼀个数组。
(function (modules) {
// ...
})([function (module, exports) {
function sayHello () {
console.log('hello');
}
}])
该函数的作⽤就是管理模块,它的内部定义了两个主要的对象 installedModules 对象和 __webpack_require__(moduleId) 函数对象。
installedModules 对象初始化代码如下。
var installedModules = {};
来看⼀下函数对象 __webpack_require__ 的定义,它的作⽤与 require 函数相同。
// require 函数
function __webapck_require__(moduleId) {
// 如果模块在缓存中
if (installedModules[moduleId]) {
return installedModules[moduleId].exports;
}webpack打包流程 面试
// 创建⼀个新的模块(并将它放在缓存中)
var module = installedModules[moduleId] = {
i: moduleId,
l: false,// l是loaded的缩写,指代是否已经加载
exports: {}
};
// 执⾏模块的函数
modules[moduleId].ports, module, ports, __webpack_require__);
// 标记模块已经加载
module.l = true;
// 返回模块的 exports 对象
ports;
}
看到这⾥,我们可以看出 installedModules 对象其实是被当作字典使⽤,key 是模块的 id,value 是代表模块状态和导出的⼀个对象,如下。
{
i: moduleId, // 模块的 id
l: false, // l是loaded的缩写,指代是否已经加载
exports: {} // 模块的导出对象
}
__webpack_require__ 还被定义了许多的静态⽅法和静态对象。
// 外放 modules 对象(__webpack_modules__)
__webpack_require__.m = modules;
// 外放模块的缓存
__webpack_require__.c = installedModules;
// 定义 getter 函数以便友好的加载模块
__webpack_require__.d = function (exports, name, getter) {
if(!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, {
configurable: false,
enumerable: true,
get: getter
});
}
};
__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) { Object.prototype.hasOwnProperty.call(object, property); };
// __webpack_public_path__
__webpack_require__.p = "";
在函数的最后,结果是导⼊ moduleId = 0 的模块,就是执⾏了⼊⼝⽂件。
return __webpack_require__(__webpack_require__.s = 0);
你定义的⼊⼝⽂件中的内容被转换为。
(function (module, __webpack_exports__, __webpack_require__) {
console.log('hello, world!');
});
传⼊⽴即函数表达式中的参数为⼀个数组对象,⽽⼊⼝⽂件转换的函数为数组中的第⼀个元素。注意 webpack 添加的注释 /* 0 */ 代表该模块的 moduleId 为 0,⽽ webpack 其实是将每⼀个资源⽂件看作⼀个模块,并将其指定⼀个标识 moduleId。
[
/* 0 */
(function(module, __webpack_exports__, __webpack_require__) {
// ...
})
]
⼆、添加依赖
1. 准备
添加⼀个新的 js ⽂件,命名为 util.js,代码如下。
export function add (a, b) {
return a + b;
}
⼊⼝⽂件 index.js 代码修改如下。
import util from './util.js'
console.log('1 + 2 = ' + util.add(1, 2));
执⾏之前的命令,使⽤ webpack 构建⼯具进⾏打包。
2. 分析
此时查看 index.bundle.js 下的代码,之后⽣成的⽴即执⾏函数的传⼊参数发⽣变化,还记得之前说过该参数为⼀个数组,此时的数组如下。
[
/* 0 */
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */var __WEBPACK_IMPORTED_MODULE_0__util_js__ = __webpack_require__(1);
console.log('1 + 2 = ' + __WEBPACK_IMPORTED_MODULE_0__util_js__["a" /* default */].add(1 + 2));
}),
/* 1 */
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony default export */ __webpack_exports__["add"] = add;
function (a, b) {
return a + b;
}
})
]
可见,webpack 将每个⽂件视为⼀个独⽴的模块,它分析模块之间的依赖关系,将模块中 import export 相关的内容做⼀次替换,⽐如。
import b from './b'
export default {
name: 'c'
}
// 最后被转化为
var __WEBPACK_IMPORTED_MODULE_0_b__ = __webpack_require__(0)
// 这⾥需要特别注意⼀点,webpack 将 a 属性作为某块的 default 值
__webpack_exports__["a"] = ({
name: 'c'
})
再给所有模块外⾯加⼀层包装函数,使其成为模块初始化函数,接着把所有模块初始化函数合成⼀个数组,赋值给 modules 变量。
三、模块的动态导⼊
1. 准备
修改⼊⼝⽂件 index.js 的代码如下。
import('./util.js').then(function (util) {
console.log('1 + 2 = ' + util.add(1 + 2));
})
执⾏之前的命令,使⽤ webpack 构建⼯具进⾏打包。
2. 分析
这次打包后不仅获取了 index.bundle.js ⽂件,还产⽣了⼀个 0.index.bundle.js ⽂件,接下来先分析 o.index.bundle.js。
webpackJsonp([0],[
/* 0 */,
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["add"] = add;
function add (a, b) {
return a + b;
}
/***/ })
]);
之后可以看到该块代码以 JSONP 的形式被加载,这⾥可以看出该代码被加载后⽴即执⾏ webpackJsonp 这个⽅法,这个⽅法为index.bundle.js 中新增的⽅法。在看这个⽅法之前,先看⼀下 index.bundle.js 中新增的⼀个对象。
var installedChunks = {
1: 0
};
该对象记录 chunk 的安装状态,⽽不记录 chunk 相关的具体内容,具体内容仍保存到 installedModules 对象中。installedChunks 也是被当作⼀个字典使⽤,key 为 chunk 的 id(此处要注意与 mouleId 的区别,每个 chunk 中⾄少会拥有⼀个module),value 可为 0(已加载)、[resolve, reject](正在加载)、undefined(未加载),可以在 requireEnsure(chunkId) ⽅法中观察到这种状态的变化。
__webpack_require__.e = function requireEnsure(chunkId) {
var installedChunkData = installedChunks[chunkId];
if (installedChunkData === 0) {
return new Promise(function(resolve) { resolve(); });
}
// ⼀个 Promise 意味“正在加载”
if (installedChunkData) {
return installedChunkData[2]; } var promise = new Promise(function(resolve, reject) { installedChunkData = installedChunks[chunkId] = [resolve, reject]; }); installedChunkData[2] = promise; var head = ElementsByTagName('head')[0]; var script = ateElement('script'); pe = 'text/javascript'; script.charset = 'utf-8'; script.async = true; script.timeout = 120000; if(__webpack_require__.nc) {
script.setAttribute("nonce", __webpack_require__.nc); } script.src = __webpack_require__.p + "" + ({}[chunkId]||chunkId) + ".bundle.js"; var timeout = setTimeout(onScriptComplete, 120000); r
= load = onScriptComplete; function onScriptComplete() { // avoid mem leaks in IE. r = load = 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); return promise; };
最后看 webpackJsonp 函数,如下。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。