webpack早就已经在前端领域大放异彩,会使用和优化webpack也已经是中、高级工程师必备技能,在此基础之上再对webpack的原理进行理解和掌握,必定会在未来的开发中事半功倍。若是对于webpack不熟悉可以查看之前的文章进行学习和了解。
由于本人能力一般、水平有限,所以会在本篇文章编写过程中对一些内容进行又臭又长的赘述,就是为了能让一些基础比较薄弱的同学阅读起来可以更加省心点,接下来即将开始正题了,希望此文章能对你有些许帮助。
构建项目
新建一个文件夹 webpack-theory
是之后插件的名字,可以理解为webpack的别名,可以直接 wepack-theory进行使用。
新建 bin 目录,在此目录下创建webpack-theory.js文件, 将打包工具主程序放入其中
主程序的顶部应当有: #!/usr/bin/env node 标识,指定程序执行环境为 node
#!/usr/bin/env node // log的内容修改直接,可以直接生效 console.log('当通过npm link链接之后,通过webpack-theory指令可以直接打出');
在package.json中配置 bin 脚本,与scripts平级
{ "bin": "./bin/webpack-theory.js" }
通过 npm link 将本地的项目webpack-theory 链接到全局包中,链接之后便可以直接在本地使用,供本地测试使用,具体参考
成功之后,可以 cd /usr/local/lib/node_modules 查看所有安装的包
进入目录后,可以看到webpack-theory,webpack-theory就是npm link时,在全局的node_modules中生成一个符号链接,指向模块(webpack-theory)的本地目录,当本地的文件(bin/webpack-theory)修改时会自动链接到全局,因为全局的node_modules只是本地的引用
在本地执行 webpack-theory, 会直接将 bin/webpack-theory.js 的console.log内容输出
>>> ~ » webpack-theory >>> 当通过npm link链接之后,通过webpack-theory指令可以直接打出 分析bundle在深入接触webpack 原理之前,需要知道其打包生成的文件结果是什么样,通过打包生成的文件可以从整体了解webpack在对文件处理过程中做了哪些事情,通过结果反推其原理。
自行创建一个简单的weback项目,创建三个js文件,分别是index.js,parent.js 和 child.js,并将其通过webpack进行打包
index.js 内容
const parent = require('./parent.js') console.log(parent)parent.js 内容
const child = require('./child.js') module.exports = { msg: '我是parent的信息', child: child.msg }child.js 内容
module.exports = { msg: '我是child的信息' }通过 npx webpack 进行打包,将打包文件进行简单的删除和整理之后
(function (modules) { // 将所有的模块组成一个modules对象传递进来, 键就是模块的路径,值就是模块内部的代码 // 模块缓存对象, 已经解析过的路径都会放进来,可以判断当前需要解析的模块是否已经解析过 var installedModules = {}; // 定义一个 webpack 自己的的 require polyfill function __webpack_require__(moduleId) { // 检测 moduleId 是否已经存在缓存中了,若是已经存在则不需要在进行依赖解析 if (installedModules[moduleId]) { return installedModules[moduleId].exports; } // 创建一个新的 module, 并将其push至缓存中,方便在后续递归遍历解析依赖时,检测是否已经解析过 var module = installedModules[moduleId] = { i: moduleId, // moduleId 是自执行函数的参数 modules 对象的键,根本是模块的路径 exports: {} }; // 执行 modules[moduleId] 函数 modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); // 将 exports 返回 return module.exports; } // 将 webpack.config.js 配置中的 entry 作为 moduleId 进行传递 return __webpack_require__("./src/index.js"); }) /*** 将项目中的几个模块作为自执行函数的参数传递 ***/ ({ // webpack.config.js 配置中 entry 的值,会将其作为递归解析依赖的入口 "./src/index.js": (function (module, exports, __webpack_require__) { eval("const parent = __webpack_require__(/*! ./parent.js */ \"./src/parent.js\")\n\nconsole.log(parent)\n\n//# sourceURL=webpack:///./src/index.js?"); }), "./src/parent.js": (function (module, exports, __webpack_require__) { eval("const child = __webpack_require__(/*! ./child.js */ \"./src/child.js\")\n\nmodule.exports = {\n msg: '我是parent的信息',\n child: child.msg\n}\n\n\n\n//# sourceURL=webpack:///./src/parent.js?"); }), "./src/child.js": (function (module, exports) { eval("\nmodule.exports = {\n msg: '我是child的信息'\n}\n\n//# sourceURL=webpack:///./src/child.js?"); }) });根据生成的bundle.js可以梳理webpack的整体打包思路,就是利用一个自执行函数创建一个闭包,在这个独立的作用域中,将模块的路径作为modules的键、模块的内容放在一个函数中作为值作为自执行函数的形参传递进来,通过自定义的函数 __webpack_require__进行递归解析。
简单分析一下bundle的整体执行过程
第一步: 自执行函数第一次执行时,会直接运行内部的__webpack_require__函数,并将入口文件的路径./src/index.js作为形参moduleId传递
第二步: 在函数__webpack_require__执行过程中