webpack 产出物存在的问题
webpack 万物皆模块的理念,每一个 js 都是一个 JavaScript 模块。一个 bundle 是由多个 module 合成的,一个 bundle 是一个 js 文件,所以 webpack 自己实现了一套 commonjs 规范用于让 js 成为一个独立的模块。
来一个 🌰: 比如说有 3 个模块,index.js、add.js、sum.js
//add.js
export default (a, b) => a + b;
// sum.js
export default arr => arr.reduce((pre, item) => pre + item, 0);
//index.js
import sum from "./sum";
import add from "./add";
let a = 1,
b = 2,
arr = [1, 2, 3, 4, 5];
console.log(add(a, b));
console.log(sum(arr));
打出来的代码如下(删除一些无用的代码):
(function (modules) {
var installedModules = {};
function __webpack_require__(moduleId) {
if (installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
var module = (installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {},
});
modules[moduleId].call(
module.exports,
module,
module.exports,
__webpack_require__
);
module.l = true;
return module.exports;
}
return __webpack_require__((__webpack_require__.s = "./src/index.js"));
})({
/***/ "./src/add.js": /***/ function (
module,
__webpack_exports__,
__webpack_require__
) {
"use strict";
eval(
'__webpack_require__.r(__webpack_exports__);\n// add.js\n/* harmony default export */ __webpack_exports__["default"] = ((a, b) => a + b);\n\n\n//# sourceURL=webpack:///./src/add.js?'
);
/***/
},
/***/ "./src/index.js": /***/ function (
module,
__webpack_exports__,
__webpack_require__
) {
"use strict";
eval(
'__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sum__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sum */ "./src/sum.js");\n/* harmony import */ var _add__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./add */ "./src/add.js");\n\n\n\nlet a = 1,\n b = 2,\n arr = [1, 2, 3, 4, 5]\n\nconsole.log(Object(_add__WEBPACK_IMPORTED_MODULE_1__["default"])(a, b))\nconsole.log(Object(_sum__WEBPACK_IMPORTED_MODULE_0__["default"])(arr))\n\n\n//# sourceURL=webpack:///./src/index.js?'
);
/***/
},
/***/ "./src/sum.js": /***/ function (
module,
__webpack_exports__,
__webpack_require__
) {
"use strict";
eval(
'__webpack_require__.r(__webpack_exports__);\n// sum.js\n/* harmony default export */ __webpack_exports__["default"] = (arr => arr.reduce((pre, item) => pre + item, 0));\n\n\n//# sourceURL=webpack:///./src/sum.js?'
);
/***/
},
});
可以看出由 webpack 打包出来的是一个大大的 IIFE,上面一大堆是 webpack 自己实现的 commonjs 规范,下面的传参是我们的代码,可以看出 webpack 把我们的代码的每一个文件使用 IIFE 来做模块化,正式因为如此,问题出来了。
大量的函数闭包包裹代码,导致体积增大,模块越来越明显,运行代码时创建的函数作用域变多,导致内存开销变大。
解决上面问题的方法就是 scope hoisting
Scope hoisting
使用 scope hoisting 有两种方式:
- 使用 webpack 自带的 ModuleConcatenationPlugin 插件开启
module.exports = {
plugins: [new webpack.optimize.ModuleConcatenationPlugin()],
};
- webpack 构建项目生产环境默认开启(mode:production)
module.exports = {
mode: "production",
};
启动一下先来看打包结果
(function (modules) {
var installedModules = {};
function __webpack_require__(moduleId) {
if (installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
var module = (installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {},
});
modules[moduleId].call(
module.exports,
module,
module.exports,
__webpack_require__
);
module.l = true;
return module.exports;
}
return __webpack_require__((__webpack_require__.s = "./src/index.js"));
})({
/***/ "./src/index.js": /***/ function (
module,
__webpack_exports__,
__webpack_require__
) {
"use strict";
eval(
"__webpack_require__.r(__webpack_exports__);\n\n// CONCATENATED MODULE: ./src/sum.js\n// sum.js\n/* harmony default export */ var sum = (arr => arr.reduce((pre, item) => pre + item, 0));\n\n// CONCATENATED MODULE: ./src/add.js\n// add.js\n/* harmony default export */ var add = ((a, b) => a + b);\n\n// CONCATENATED MODULE: ./src/index.js\n\n\n\nlet a = 1,\n b = 2,\n arr = [1, 2, 3, 4, 5]\n\nconsole.log(add(a, b))\nconsole.log(sum(arr))\n\n\n//# sourceURL=webpack:///./src/index.js_+_2_modules?"
);
/***/
},
});
你会发现,使用 scope hoisting 方式,IIFE 变成一个了(原来有 3 个),add.js 和 sum.js 里面的代码被打包到 了 index.js 中,减少了 IIFE 的数量。
原理: scope hoisting 将所有引入的模块按照顺序放在一个函数作用域里面,然后适当的重命名一些变量以防止变量名冲突。(总结一句:引用一次就内联进来,减少 IIFE 的数量)
总结
以上就是 IIFE 的使用方式,对比一下没有 scope hoisting 的状态:减少了函数声明代码从而减少了函数作用域的生成,降低了内存开销。