因此这里 webpack 利用 CommonJS/ ES Modules 规范进行了处理。使得各个模块之间相互引用无需考虑最终实际呈现的顺序。最终会被打包为一个 bunlde 模块,无需按照顺序手动引入。
baz.js
const bar = require(‘./bar.js’);
module.exports = function baz (){
…
bar();
…
}
abc.js
const bar = require(‘./bar.js’);
module.exports = function baz (){
…
bar();
…
}
image-20200627003815071
webpack 的模块化机制与实现
基于以上两项特性,模块的隔离以及模块的依赖聚合。我们现在可以非常清晰的知道了webpack所起的核心作用。
-
为了尽可能降低编写的难度和理解成本,我没有使用 AST 的解析,(当然 AST 也不是什么很难的东西,以后的文章中我会讲解 AST是什么以及 AST 解析器的实现过程。
-
仅实现了 CommonJS 的支持
bundle工作原理
为了能够实现 webpack, 我们可以通过反推的方法,先看webpack 打包后 bundle 是如何工作的。
「源文件」
// index.js
const b = require(‘./b’);
b();
// b.js
module.exports = function () {
console.log(11);
}
「build 后」(去除了一些干扰代码)
(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 = 0));
})([
/* 0 */
function(module, exports, webpack_require) {
var b = webpack_require(1);
b();
},
/* 1 */
function(module, exports) {
module.exports = function() {
console.log(11);
};
},
]);
image-20200627135324956
以上就是 bundle 的运作原理。通过上述的流程图我们可以看到,有四个关键点
-
已注册模块(存放已经注册的模块)
-
模块列表(用来存放所有的包装模块)
-
模块查找(从原来的树形的模块依赖,变成了扁平查找)
-
模块的包装(原有的模块都进行了一次包装)
webpack实现
通过 bundle 的分析,我们只需要做的就是 4 件事
-
遍历出所有的模块
-
模块包装
-
提供注册模块、模块列表变量和导入函数
-
持久化导出
模块的遍历
首先来介绍一下模块的结构,能使我们快速有所了解, 结构比较简单,由内容和模块id组成。
interface GraphStruct {
context: string;
moduleId: string;
}
{
“context”: `function(module, exports, require) {
const bar = require(‘./bar.js’);
const foo = require(‘./foo.js’);
console.log(bar());
foo();
}`,
“moduleId”: “./example/index.js”
}
接下来我们以拿到一个入口文件来进行讲解,当拿到一个入口文件时,我们需要对其依赖进行分析。说简单点就是拿到 require
中的值,以便我们去寻找下一个模块。由于在这一部分不想引入额外的知识,开头也说了,一般采用的是 AST
解析的方式,来获取 require
的模块,在这里我们使用正则。
用来匹配全局的 require
const REQUIRE_REG_GLOBAL = /require((“|')(.+)(”|'))/g;
用来匹配 require 中的内容
const REQUIRE_REG_SINGLE = /require((“|')(.+)(”|'))/;
const context = `
const bar = require(‘./bar.js’);
const foo = require(‘./foo.js’);
console.log(bar());
foo();
`;
console.log(context.match(REQUIRE_REG_GLOBAL));
// [“require(‘./bar.js’)”, “require(‘./foo.js’)”]
image-20200627202427794
由于模块的遍历并不是只有单纯的一层结构,一般为树形结构,因此在这里我采用了深度遍历。主要通过正则去匹配出require
中的依赖项,然后不断递归去获取模块,最后将通过深度遍历到的模块以数组形式存储。(不理解深度遍历,可以理解为递归获取模块)
image-20200627142130902
以下是代码实现
…
private entryPath: string
private graph: GraphStruct[]
…
createGraph(rootPath: string, relativePath: string) {
// 通过获取文件内容
const context = fs.readFileSync(rootPath, ‘utf-8’);
// 匹配出依赖关系
const childrens = context.match(REQUIRE_REG_GLOBAL);
// 将当前的模块存储下来
this.graph.push({
context,
moduleId: relativePath,
})
const dirname = path.dirname(rootPath);
if (childrens) {
// 如有有依赖,就进行递归
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:前端)
基础面试题
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
主要内容包括:HTML,CSS,JavaScript,浏览器,性能优化等等
75547)]
[外链图片转存中…(img-yYs9pdwV-1710701575548)]
[外链图片转存中…(img-WamhWAsP-1710701575549)]
[外链图片转存中…(img-bxHrh8kz-1710701575549)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:前端)
[外链图片转存中…(img-dgJMiGPV-1710701575550)]
基础面试题
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
主要内容包括:HTML,CSS,JavaScript,浏览器,性能优化等等