Skip to content

在使用 Rollup 进行打包时,manualChunks 选项可以帮助你手动控制代码拆分(code splitting),这对于优化大文件的加载性能非常有用。通过合理地拆分代码,可以减少初始加载时间,并提高应用的整体性能。

manualChunks 简介

manualChunks 是 Rollup 配置中的一个选项,允许你根据模块路径或其他逻辑将代码拆分为多个 chunk 文件。这比自动拆分提供了更多的控制,特别是在处理大型项目时。

使用 manualChunks 拆分大文件

以下是一个示例配置,展示了如何使用 manualChunks 来拆分大文件:

示例配置

javascript
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import terser from '@rollup/plugin-terser';

export default {
  input: 'src/main.js',
  output: {
    dir: 'dist',
    format: 'es',
    entryFileNames: '[name].js',
    chunkFileNames: '[name]-[hash].js',
  },
  plugins: [
    resolve(),
    commonjs(),
    terser(),
  ],
  manualChunks(id) {
    // 根据模块路径进行拆分
    if (id.includes('node_modules')) {
      // 将所有 node_modules 中的依赖打包到 vendor.js 中
      return 'vendor';
    }

    if (id.includes('src/utils')) {
      // 将 utils 目录下的模块打包到 utils.js 中
      return 'utils';
    }

    if (id.includes('src/components')) {
      // 将 components 目录下的模块打包到 components.js 中
      return 'components';
    }

    // 其他模块保持默认行为
  }
};

这样vendor打包的js文件可能会很大,如果你使用了pnpm可能还会把node_modules/.pnpm的文件打包进去,如何避免?

js
export default defineConfig({
  ...
  build: {
    rollupOptions: {
      output: {
        chunkFileNames: "static/js/[name]-[hash].js",
        entryFileNames: "static/js/[name]-[hash].js",
        assetFileNames: "static/[ext]/[name]-[hash].[ext]",
        // manualChunks(id) {
        //   if (id.includes("node_modules") && !id.includes(".pnpm")) {
        //     return id
        //       .toString()
        //       .split("node_modules/")[1]
        //       .split("/")[0]
        //       .toString();
        //   }
        // },
        manualChunks: {
          'element-plus': ['element-plus'],
          echarts: ['echarts'],
        }      
      },
    },
  },
 ...
})

关键点解释

  1. input: 指定入口文件。
  2. output:
    • dir: 输出目录。
    • format: 输出格式(如 ES 模块)。
    • entryFileNames: 入口文件的命名规则。
    • chunkFileNames: 动态导入或拆分出的 chunk 文件的命名规则。
  3. plugins: 使用的插件,如 @rollup/plugin-node-resolve@rollup/plugin-commonjs@rollup/plugin-terser
  4. manualChunks: 自定义拆分逻辑:
    • 如果模块路径包含 node_modules,则将其打包到 vendor.js 中。
    • 如果模块路径包含 src/utils,则将其打包到 utils.js 中。
    • 如果模块路径包含 src/components,则将其打包到 components.js 中。
    • 对于其他模块,默认行为是保持不变。

进一步优化

1. 动态导入

你可以结合动态导入 (import()) 来进一步优化代码拆分。例如:

javascript
// 在 main.js 中
const loadComponent = async () => {
  const module = await import('./components/MyComponent');
  return new module.default();
};

Rollup 会自动识别这种动态导入,并将其拆分为单独的 chunk 文件。

2. 基于大小的拆分

如果你希望根据模块大小进行拆分,可以结合 treeshakeoutput.manualChunks 的逻辑来实现。例如:

javascript
manualChunks(id) {
  const sizeThreshold = 50 * 1024; // 50KB
  const moduleSize = calculateModuleSize(id); // 假设有一个函数计算模块大小

  if (moduleSize > sizeThreshold) {
    return 'large-modules';
  }

  if (id.includes('node_modules')) {
    return 'vendor';
  }

  // 其他逻辑...
}

注意:上述代码中的 calculateModuleSize 是假设的一个函数,实际中你需要实现或找到合适的工具来计算模块大小。

3. 第三方库的拆分

对于第三方库,通常建议将它们打包到一个单独的 chunk 文件中,以避免重复加载。例如:

javascript
manualChunks(id) {
  if (id.includes('node_modules')) {
    // 将所有 node_modules 中的依赖打包到 vendor.js 中
    return 'vendor';
  }

  // 其他逻辑...
}

总结

通过 manualChunks,你可以灵活地控制 Rollup 如何拆分你的代码。合理的拆分策略不仅可以提升应用的加载速度,还能改善用户体验。以下是一些最佳实践:

  • 按功能模块拆分:将不同功能模块拆分为独立的 chunk 文件。
  • 第三方库分离:将第三方库打包到单独的 chunk 文件中,减少重复加载。
  • 动态导入:结合动态导入进一步优化代码拆分。

Released under the MIT License.