目前技术理解 webpack 打包做了什么
读取转换文件
- 用 fs 模块读取以及写入文件
- 获取打包命令行参数,通过 process.argv
- 读取文件后,替换文件内容主要依据是根据 ast 抽象语法分析判断,如将 import 语句,转换成 webpack_require,最后写入文件
const fs = require("fs");
const path = require("path");
const acorn = require("acorn"); // 抽象语法分析插件
async function startWork () {
const fileBuffer = await fs.promises.readFile(path.resolve(__dirname,"../src/index.js"));
const fileString = fileBuffer.toString();
// console.log('buffer=', fileBuffer);
// console.log('fileString=', fileString);
// fs.promises.writeFile(path.resolve(__dirname, "../src/main.js"), fileString.replace("import", "webpack_require"))
// webpack 做这些替换前会做 ast 抽象语法分析
const result = acorn.parse(fileString, { ecmaVersion: "2020", sourceType: "module" });
// console.log('ast result=', result.body);
const importStr = fileString.substring(27, 68)
// console.log('声明=', importStr)
const replaceStr = fileString.replace(importStr, "")
// console.log('替换后=', replaceStr)
// console.log('命令行参数', process.argv)
const [fst, sec, ...argsFromUser] = process.argv
// console.log(fst)
// console.log(sec)
// console.log(argsFromUser)
}
startWork();
打包主要步骤:
模块解析
Webpack 从指定的入口文件(entry point)开始,通过解析模块之间的依赖关系。它能够识别各种类型的模块,如 JavaScript、CSS、图片、JSON 等。
例如,在 JavaScript 中使用 import 或 require 语句来引入其他模块,Webpack 会解析这些语句以确定依赖关系。
构建依赖图
根据解析出的模块依赖关系,Webpack 构建一个依赖图(Dependency Graph)。这个图描述了各个模块之间的引用和依赖关系。
代码转换
对模块中的代码进行转换处理。例如,使用 Loader 来处理不同类型的文件,如将 ES6 代码转换为 ES5,处理 CSS 文件,将图片转换为 Data URL 等。
模块合并
将所有相关的模块按照依赖关系进行合并,生成最终的输出文件。
代码优化
在打包过程中进行一些优化操作,如代码压缩、去除未使用的代码(Tree Shaking)、提取公共模块等,以减小输出文件的大小,提高性能。
输出打包结果
将处理和优化后的代码输出到指定的目录和文件中,通常会生成一个或多个打包后的文件,如一个包含所有代码的 bundle.js 文件,或者根据配置生成多个代码块。
结果解析
- webpack 打包结果是一个 iife(立即执行函数)。基于 webpack_require 和 webpack_exports 对象的一套模块化规范,把源码中的文件作为一个个 key + function 的形式存储在 webpack_modules,加载的时候执行 webpack_modules 里对应的 key 的函数,函数执行完以后把对应的 exports 对象写入值,默认导出会有一个 default 属性在 exports 对象上,具名导出就是对应的 key 在exports 对象上
- webpack_require 会返回写入值的 exports 对象
- 拿到对应的 exports 对象
{
default: () => __WEBPACK_DEFAULT_EXPORT__,
printHello: () => /* binding */ printHello,
}
- 使用的时候,将原来的模块方式,改成对象 .key 使用方式。打包示例
(() => {
"use strict";
var __webpack_modules__ = {
"./src/utils.js": (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
) => {
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
default: () => __WEBPACK_DEFAULT_EXPORT__,
printHello: () => /* binding */ printHello,
});
const printHello = () => {
console.log("Hello");
};
const key = "yes";
const __WEBPACK_DEFAULT_EXPORT__ = key;
},
};
// 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 });
};
})();
var __webpack_exports__ = {};
/*!**********************!*\
!*** ./src/index.js ***!
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_0__ =
__webpack_require__(/*! ./utils */ "./src/utils.js");
// import _ from 'lodash';
function component() {
const element = document.createElement("div");
// Lodash, currently included via a script, is required for this line to work
// element.innerHTML = _.join(['Hello', 'webpack'], ' ');
(0, _utils__WEBPACK_IMPORTED_MODULE_0__.printHello)();
console.log(_utils__WEBPACK_IMPORTED_MODULE_0__["default"]);
return element;
}
document.body.appendChild(component());
})();
Webpack 4和5
5内置了插件 clean-webpack-plugin,清除打包目录