共计 2692 个字符,预计需要花费 7 分钟才能阅读完成。
Webpack 模块化原理图解
为什么须要模块化
场景 1
A 同学开发了模块 a,B 同学开发了模块 b,在页面下通过如下形式进行援用
<script src="a.js"></script>
<script src="b.js"></script>
这时模块 a,模板 b 中的代码都裸露在全局环境中,如果模块 a 中定义了一个办法del
。同学 b 并不知道,在模块 b 中也定义了一个办法del
。这时便造成了命名抵触的的问题。
场景 2
C 同学开发了一个公共的工具库 utils.js
,D 同学开发了一个公共的组件tab.js
,tab.js
依赖utils.js
。同学 E 须要应用 D 同学开发的tab.js
,就须要通过如下形式援用
<script src="util.js"></script>
<script src="tab.js"></script>
同学 E 本人也开发了一个 dailog.js
同时它也依赖 util.js
。当初页面同时援用了dialog.js
和tab.js
,代码如下
<script src="util.js"></script>
<script src="dialog.js"></script>
<scrpt src="tab.js"></script>
同学 E 不仅须要同时援用这三个 js 文件,还必须保障文件之间的援用程序是正确的。同时,从下面的代码咱们无奈间接看出模块与模块之间的依赖关系,如果不深刻 tab.js
,咱们无奈晓得tab.js
到底是只依赖 util.js
还是 dialog.js
或者两者都依赖。随着我的项目逐步增大,不同模块之间的依赖关系则会变的越来越难以保护也会导致许多模块中大量的变量都裸露在全局环境中。
模块化的几种实现计划
模块化的标准有很多种,如下
| 标准 | 实现计划 |
| — | — |
| CommonJS | node.js |
| AMD | Require.js |
| CMD | Sea.js
| UMD | |
| ES6 Module | |
webpack 反对 CommonJS,AMD,ESModule 等多种模块化形式的语法
webpack 的模块化原理图解
在 webpack 中,所有皆模块。上面咱们通过 webpack 来打包以下代码
目录构造如下:
代码如下:
// webpack.config.js
const path = require('path');
module.exports = {
entry: 'a.js',
output: {path: path.resolve(__dirname, "dist"),
filename: "[name].js"
},
resolve: {modules: [path.resolve(__dirname)]
},
optimization: {minimize: false}
}
// a.js
var b = require('b');
module.exports = b.text + 'world';
// b.js
exports.text = 'hello';
在 simple 目录下执行 webpack
命令,会在 simple 目录下生成 dist/output.js
文件。
// outout.js
// 代码及正文如下
(() => {
// 所有导入的模块都存储在__webpack_modules__对象中,并且每个模块都要一个标识该模块的 id
var __webpack_modules__ = ({847: ((module, __unsed_webpack_exports, __webpack_require__) => {// 模块 a...}),
996: ((__unused_webpack_module, exports) => {// 模块 b...})
})
var __webpack_module_cache__ = {};
function __webpack_require__(moduleId) {
// 查看缓存中不存在 847 导出对象,避免模块 847 屡次执行
if (__webpack_module_cache__[moduleId]) {
// 从缓存中返回 847 导出对象
return __webpack_module_cache__[moduleId].exports;
}
// 1. 创立 {exports: {}} 对象
// 2. 像缓存中增加该对象,并让该对象与模块 id 847 相关联
var module = __webpack_module_cache__[moduleId] = {exports: {}
}
// 3. 通过__webpack_modules__查问模块 847
// 4. 执行模块 847 并传入刚刚创立的模块 847 的导出对象 module,以及 module.exports 等
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
// 5. 返回模块 847 的导出对象
return module.exports;
}
// 导入模块 847
__webpack_require__(847);
})()
如上代码咱们能够晓得,所有的的模块都存储在 __webpack_modules__
对象中,模块的导出对象则存储在 __webpack_module_cache__
对象中,咱们定义的模块都能够通过传入 moduleId
来调用 __webpack_require__
接口来拜访该模块以及该模块的导出对象。
当咱们通过 __webpack_require__
接口拜访模块 847
的导出对象时。会先判断 __webpack_module_cache__
对象中有没有该模块的导出对象,如果有的话,则间接返回。如果没有的话则会
- (1)创立模块 847 的导出对象
var module = {exports: {}
}
- (2)将该导出对象增加到
__webpack_module_cache__
对象中
__webpack_module_cahce__[moduleId] = module
- (3)(4)通过
__webpack_modules__
查问模块 847 并执行
// 传入模块 847 的导出对象,以及 require 接口
__webpack_modules__[moduleId](module, module.exports, __webpack_require__)
- (5)返回模块 847 的导出对象
return module.exports
图解如下:
- 橘黄色背景的元素代表数据
- 绿色背景的元素代表函数
总结
模块与模块之间通过require
接口进行通信。
- 模块第 1 次被援用时,调用
require
接口会像cache
中增加导出对象。并返回增加的导出对象。 - 模块被第 2 + 次援用时,调用
require
接口查问cache
对象时,返回cache
中对应的导出对象