背景:
这几天重新梳理了一下 webpack异步加载的原理,并对实现细节进行了一番拆解,再次让我感叹:真是万变不离其宗,基础知识真的是构建上层建筑的坚实底座,在此也分享给大家,希望大家可以领略到webpack实现异步加载之美
基座一:webpack模块化方案
你可能也吐槽过 webpack产物为啥这么丑?
原始源代码
javascript
// src/index.js (入口文件)
let title = require('./title.js')
console.log(title);
// src/title.js
module.exports = 'bu';
构建后产物结构概览
javascript
/******/ (() => { // webpack bootstrap 启动函数
/******/ var __webpack_modules__ = ([
/* 0 */ /* title.js 模块 */
((module) => {
module.exports = 'bu';
}),
/* 1 */ /* index.js 模块 */
((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {
let title = __webpack_require__(0); // 加载模块0
console.log(title);
})
/******/ ]);
/******/ /* 模块缓存 */
/******/ var __webpack_module_cache__ = {};
/******/ /* Webpack 自实现的 require 函数 */
/******/ function __webpack_require__(moduleId) {
/******/ /* 检查缓存 */
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ /* 创建新模块并加入缓存 */
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ exports: {}
/******/ };
/******/ /* 执行模块函数 */
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/ /* 返回模块的 exports 对象 */
/******/ return module.exports;
/******/ }
/******/ /* 启动入口模块 */
/******/ return __webpack_require__(1); // 加载入口模块(index.js)
/******/ })()
;
✅ 核心原因:规范转换成本(模块系统适配)
浏览器原生不支持模块规范
浏览器无法直接执行 CommonJS/AMP/UMD 模块语法(如 require() / module.exports / define())。Webpack 必须将这些规范统一转换为浏览器可执行的函数包装形式。
-
整个打包产物 被包裹在一个外层 IIFE 中(立即执行)
- 意义:创建独立作用域
javascript// 未包裹:变量暴露全局 var utils = {...} // 可能覆盖其他脚本的同名变量 // IIFE 包裹后: (function() { var utils = {...} // 安全隔离 })(); -
每个模块 被转换为标准函数(非立即执行),作为参数传递给运行时:function(module, exports...)
- 意义 :模块环境隔离 -每次调用模块函数时都会创建新的:
javascriptconst module = { exports: {} }; modules[moduleId].call(module.exports, ...); -
模块路径被替换为 数字 ID
- 提升性能 & 减少体积
- ✅ 大幅缩短引用路径 :
'./src/utils/string-format.js'→__webpack_require__(17) - ✅ 避免路径解析开销:浏览器无需处理文件路径逻辑
- ✅ 大幅缩短引用路径 :
- 提升性能 & 减少体积
-
原生
require/module被替换为 Webpack 自实现的__webpack_require__函数- 规范统一:将 ESM/CommonJS/AMD 转为浏览器可执行格式
-
生成复杂的 运行时(runtime)代码 处理模块加载/缓存
- 避免重复执行(如多次
require同一模块)
- 避免重复执行(如多次
-
模块字典(所有代码被打包成键值对(键:上面提到的数字ID,值:上面提到的每个模块 被转换的标准函数))
- ✅ 快速索引:通过数字 ID 实现 O(1) 复杂度的模块查找
基座二:jsonp
传统的 JSONP 流程:
sequenceDiagram
participant Client as 客户端
participant Server as 服务器
Client->>Client: 创建回调函数
Note right of Client: window.myCallback =
function(data){...} Client->>Server: 动态创建
function(data){...} Client->>Server: 动态创建