这是我参与8月更文挑战的第16天,活动详情查看:8月更文挑战
前言
webpack是前端模块化打包工具,我们在使用vue2或者react的官方脚手架生成的项目里面,里面都有webpack的影子。
但是这些脚手架都是提供打包命令,我们只需运行这些打包命令就可以打包,一般不需要关心打包后的内容是怎样的。
今天想借着这次机会,一起来看看wepback打包后的源码是怎样的,分析一下。
打包
-
安装webapck
yarn add webpack webpack-cli -D // 这里你也可以使用npm我安装的版本是
"webpack": "^5.42.1","webpack-cli": "^4.7.2"。 -
编写
webpack.config.js,webpack的配置文件webpack.config.js
const path = require('path') module.exports = { entry: ['./export/index.mjs'], // 入口 output: { path: path.resolve(__dirname, 'dist'), // 输出目录 filename: 'bundle.js' //输出文件名称 } }index.mjs, 这里只引入一个简单模块,算是最简单的代码
import { name } from './test.mjs' console.log(name)test.mjs
var name = '答案cp3' export { name } -
然后在终端运行命令
webpack --mode development(基于wepback全局安装),如果不是全局的在package.json添加scripts字段,把该命令写入,通过yarn或者npm来执行,这里要写development(开发模式),这样打包出来的代码不是压缩后的,方便阅读。打包成功,我们来看看打包后的代码(去掉部分注释代码)
(() => { "use strict"; var __webpack_modules__ = ({ "./export/index.mjs": ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _test_mjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./test.mjs */ \"./export/test.mjs\");\n\r\nconsole.log(_test_mjs__WEBPACK_IMPORTED_MODULE_0__.name)\r\n\n\n//# sourceURL=webpack://test/./export/index.mjs?"); }), "./export/test.mjs": ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"name\": () => (/* binding */ name)\n/* harmony export */ });\nvar name = '答案cp3'\r\n\r\n\n\n//# sourceURL=webpack://test/./export/test.mjs?"); }) }); // The module cache var __webpack_module_cache__ = {}; // The require function function __webpack_require__ (moduleId) { // Check if module is in cache var cachedModule = __webpack_module_cache__[moduleId]; if (cachedModule !== undefined) { return cachedModule.exports; } // Create a new module (and put it into the cache) var module = __webpack_module_cache__[moduleId] = { // no module.id needed // no module.loaded needed exports: {} }; // Execute the module function __webpack_modules__[moduleId](module, module.exports, __webpack_require__); // Return the exports of the module return module.exports; } /************************************************************************/ /* webpack/runtime/define property getters */ (() => { // define getter functions for harmony exports __webpack_require__.d = (exports, definition) => { for (var key in definition) { if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); } } }; })(); /* webpack/runtime/hasOwnProperty shorthand */ (() => { __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) })(); /* webpack/runtime/make namespace object */ (() => { // define __esModule on exports __webpack_require__.r = (exports) => { if (typeof Symbol !== 'undefined' && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); } Object.defineProperty(exports, '__esModule', { value: true }); }; })(); /************************************************************************/ // startup // Load entry module and return exports // This entry module can't be inlined because the eval devtool is used. var __webpack_exports__ = __webpack_require__("./export/index.mjs"); })();这些打包生成的代码可以直接在浏览器的控制台上面运行,不过需要在
https协议下运行,因为浏览器对eval函数的限制。
分析过程
下面来解析一下这段打包后的代码:
-
所有的代码都是包裹在立即执行的匿名函数里面, 采用严格模式(es6模块打包默认采用严格模式)
-
定义
__webpack_modules__对象,里面key是各个模块的路径,value是由一个箭头函数, 里面包裹着的各个模块的代码,在webpack的开发模式下,模块代码使用eval函数执行。 -
定义
__webpack_module_cache__对象,它是用来缓存各个模块执行后的代码,key是各个模块的路径。 -
定义
__webpack_require__函数,这个是相当于模块执行函数。参数是传入模块的路径,新建个对象,把它赋值到__webpack_module_cache__下,key是传入的模块的路径,然后去第二步声明的__webpack_modules__对象找到对应的模块代码,然后执行,执行的过程把新的的对象传入,把导出的值都定义到该对象上,只读,不可写。新对象的值也会同步到__webpack_module_cache__上。 -
如果重复调用
__webpack_require__,可以根据传入的路径通过__webpack_module_cache__缓存对象找到对应的缓存值,然后返回。 -
__webpack_require__函数上定义几个方法-
d方法内部使用
defineProperty方法,定义导出模块上的属性 -
o方法利用hasOwnProperty,判断是否本身的属性
-
r方法给导出模块的各个对象都加上
__esModule属性
-
-
执行
__webpack_require__函数,参数是webpack.config.js的配置的入口模块(entry)
总结
以上就是对webpack打包ES6模块的源码分析,这个是比较简单的模块依赖,不过大体思路应该是这样,大家如果有不懂的地方,跟着步骤去打包,然后把打包的代码在控制台上执行,加断点打印结果。
感谢你们的阅读。