runtime chunk 到底是什么?

12 阅读2分钟

runtime chunk(运行时代码块)是 Webpack 生成的一小段核心代码,它不包含你的业务逻辑,而是负责:

  • 管理模块之间的依赖关系(比如哪个模块对应哪个文件);
  • 加载和执行打包后的模块(比如异步加载 chunk);
  • 维护模块的缓存和版本映射。

实操案例:从零看 runtime chunk 的生成

1. 准备极简项目结构

plaintext

├── src
│   ├── index.js       # 主入口
│   └── utils.js       # 工具模块
├── package.json
└── webpack.config.js

2. 编写业务代码

javascript

运行

// src/utils.js
export const add = (a, b) => a + b;

javascript

运行

// src/index.js
// 同步引入 + 异步引入,触发Webpack的模块管理逻辑
import { add } from './utils.js';
console.log('同步调用:', add(1, 2));

// 异步引入(关键:会让runtime逻辑更明显)
setTimeout(() => {
  import('./async-module.js').then(({ sayHello }) => {
    sayHello();
  });
}, 1000);

javascript

运行

// src/async-module.js
export const sayHello = () => console.log('异步模块加载成功!');

3. Webpack 配置(默认开启 runtime chunk)

javascript

运行

// webpack.config.js
const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: '[name].bundle.js', // 主bundle
    chunkFilename: '[name].chunk.js', // 异步chunk
    path: path.resolve(__dirname, 'dist'),
  },
  optimization: {
    // 默认值为'single':将runtime提取为单独的chunk
    runtimeChunk: 'single', 
    splitChunks: {
      chunks: 'all', // 分割同步/异步chunk
    },
  },
};

4. 执行打包 & 查看输出

运行 npx webpack 后,dist 目录会生成 3 个文件:

plaintext

dist/
├── runtime.bundle.js   # runtime chunk(核心!)
├── main.bundle.js      # 主业务chunk(index + utils)
└── async-module.chunk.js # 异步chunk

5. 核心:runtime.bundle.js 里有什么?

打开 runtime.bundle.js,核心内容(简化后)如下:

javascript

运行

// runtime的核心逻辑:模块映射 + 加载器
(() => {
  // 1. 模块ID和文件路径的映射表(关键)
  const moduleMap = {
    "./src/async-module.js": () => import("./async-module.chunk.js"),
  };

  // 2. 模块加载器:处理异步import的核心逻辑
  window.__webpack_require__.e = (chunkId) => {
    // 加载对应的chunk文件(比如async-module.chunk.js)
    // 处理模块缓存、依赖解析
  };

  // 3. 模块缓存管理:避免重复加载
  const installedModules = {};
  window.__webpack_require__.c = installedModules;
})();

对比:禁用 runtime chunk 的效果

修改 Webpack 配置,将 runtimeChunk: false,重新打包后:

  • dist 目录只有 2 个文件:main.bundle.jsasync-module.chunk.js
  • main.bundle.js 里会包含原本 runtime.bundle.js 的所有逻辑(模块映射、加载器等);
  • 此时 main.bundle.js = 业务代码 + runtime 代码(即多个 chunk 合并到一个 bundle,对应上一轮你问的场景)。

为什么要单独提取 runtime chunk?

这是最关键的实战价值,用一个场景说明:假设你只修改了 utils.js 里的 add 函数(比如改成 a + b + 1),重新打包后:

  • main.bundle.js 的内容变了 → hash 值会变;
  • async-module.chunk.js 没改 → hash 值不变;
  • runtime.bundle.js 里的模块映射表没改 → hash 值不变;

用户浏览器缓存中:

  • 只会重新加载 main.bundle.jsruntime.bundle.jsasync-module.chunk.js 会复用缓存;

如果不提取 runtime chunk,main.bundle.js 包含 runtime 逻辑,哪怕只改一行业务代码,整个 main.bundle.js 的 hash 都变,用户需要重新加载全部内容 → 缓存失效,性能变差。


总结

  1. runtime chunk 本质:Webpack 的 “模块调度器”,包含模块映射、加载逻辑、缓存管理,无业务代码;
  2. 核心作用:管理打包后模块的加载和依赖,单独提取可提升缓存复用率;
  3. 表现形式:默认会生成单独的 runtime.bundle.js,禁用则合并到主 bundle 中(多 chunk→单 bundle)。