Webpack 在处理异步 JS 文件请求时,主要通过以下几个机制来获取和加载 JS Bundle:
Webpack 通过 import() 语法实现动态导入,将代码分割成多个 bundle:
// 动态导入 - Webpack 会自动分割代码
import('./module').then(module => {
module.doSomething();
});
<script> 标签加载 chunkwebpackJsonp 回调函数接收模块// Webpack 生成的 runtime 代码示例
__webpack_require__.e = function requireEnsure(chunkId) {
var promises = [];
// 1. 通过 JSONP 加载 chunk
var script = document.createElement('script');
script.src = jsonpScriptSrc(chunkId);
// 2. 设置回调函数
window["webpackJsonp"] = window["webpackJsonp"] || [];
window["webpackJsonp"].push = function(data) {
// 处理加载的模块
};
}
Webpack 5 使用更现代的加载方式:
// 使用 promise-based 加载
__webpack_require__.f.j = function(chunkId, promises) {
// 创建加载 promise
var promise = new Promise(function(resolve, reject) {
// 加载 chunk 文件
var url = __webpack_require__.p + chunkId + ".js";
loadScript(url, resolve, reject);
});
promises.push(promise);
};
// Webpack 编译时分析依赖
// 生成 chunk 映射表
{
"src_utils_math_js": 0,
"src_component_Modal_js": 1
}
Webpack 生成包含 chunk 信息的 runtime:
// runtime 包含的 chunk 映射信息
(self["webpackChunk"] = self["webpackChunk"] || []).push([
["chunk-name"],
{
"./src/module.js": function(module, exports) {
// 模块代码
}
}
]);
// 1. 检查缓存
if(installedChunks[chunkId] !== undefined) {
return Promise.resolve();
}
// 2. 创建加载 promise
installedChunks[chunkId] = [resolve, reject];
// 3. 创建 script 标签加载
var script = document.createElement('script');
script.src = chunkUrl;
// 4. 加载完成后执行
script.onload = function() {
// 触发 webpackJsonp 回调
// 解析模块并执行
};
import(/* webpackPrefetch: true */ './Modal');
import(/* webpackPreload: true */ './Chart');
// 指定 chunk 名称
import(/* webpackChunkName: "my-chunk" */ './module');
// 指定加载模式
import(/* webpackMode: "lazy" */ './module');
// webpack.config.js
module.exports = {
output: {
// 使用 [contenthash] 实现长效缓存
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].chunk.js',
// 指定 chunk 加载路径
publicPath: 'https://cdn.example.com/assets/',
// Webpack 5 新增
chunkLoadingGlobal: 'myCustomChunkLoadingFunction'
},
optimization: {
// 分离 runtime 代码
runtimeChunk: 'single',
// 分割策略
splitChunks: {
chunks: 'all',
minSize: 20000,
maxSize: 244000
}
}
};
<!-- 初始加载的 HTML -->
<script src="runtime.bundle.js"></script>
<script src="main.bundle.js"></script>
<!-- 动态加载时 Webpack 注入 -->
<script src="1.chunk.js" async></script>
// 查看 chunk 加载状态
console.log(__webpack_modules__);
console.log(__webpack_chunk_load__);
// 使用 webpack 的 stats 分析
npx webpack --profile --json > stats.json
Webpack 的异步加载机制通过:
编译时分析依赖关系生成 chunk 映射 运行时动态加载通过 script 标签或 fetch 模块注册通过全局回调函数或 Promise 缓存优化避免重复加载这种机制实现了按需加载,优化了首屏加载时间,同时保持了模块系统的完整性。