MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Webpack SplitChunksPlugin 的配置技巧

2022-10-152.9k 阅读

Webpack SplitChunksPlugin 的基本概念

在前端开发中,随着项目规模的增长,打包后的文件体积也会逐渐变大。这不仅会导致页面加载时间变长,还会影响用户体验。Webpack 的 SplitChunksPlugin 就是为了解决这个问题而诞生的。它能够将公共代码从各个入口 chunk 中分离出来,形成单独的 chunk,这样在多个页面或模块中就可以共享这些代码,从而减少整体的文件体积。

SplitChunksPlugin 是 Webpack 4 开始内置的插件,无需额外安装。它基于一些配置规则来决定如何拆分代码块。默认情况下,Webpack 会对异步加载的模块进行代码分割,而对于同步加载的模块则不会自动分割。通过配置 SplitChunksPlugin,我们可以对同步和异步模块都进行灵活的代码分割。

配置参数详解

  1. chunks 这个参数用于指定哪些 chunk 应该被分割。它有三个取值:'initial''async''all'
    • 'async':默认值,表示只对异步加载的 chunk 进行分割。例如,在使用动态 import() 语法引入模块时,这些异步加载的模块会根据配置进行代码分割。
    • 'initial':表示只对入口 chunk(即通过 entry 配置的入口文件)进行分割。
    • 'all':表示对所有的 chunk,包括同步和异步加载的 chunk 都进行分割。

示例代码:

module.exports = {
    //...其他配置
    optimization: {
        splitChunks: {
            chunks: 'all'
        }
    }
};
  1. minSize 这个参数定义了被分割出来的 chunk 的最小大小(以字节为单位)。如果一个模块或模块组的大小小于 minSize,则不会被分割成单独的 chunk。默认值是 30000(即 30KB)。 例如,我们将 minSize 设为 10000(10KB),表示只要模块或模块组大小超过 10KB 就有可能被分割:
module.exports = {
    //...其他配置
    optimization: {
        splitChunks: {
            chunks: 'all',
            minSize: 10000
        }
    }
};
  1. maxSizeminSize 相反,maxSize 用于限制分割后的 chunk 的最大大小。如果一个模块或模块组超过了 maxSize,Webpack 会尝试将其进一步分割,以确保每个 chunk 的大小不超过这个值。默认情况下没有设置 maxSize。 例如,我们设置 maxSize20000(20KB):
module.exports = {
    //...其他配置
    optimization: {
        splitChunks: {
            chunks: 'all',
            minSize: 10000,
            maxSize: 20000
        }
    }
};
  1. minChunks minChunks 定义了一个模块至少需要被引用多少次才会被提取到公共 chunk 中。默认值是 1,表示只要被引用一次就可能被提取。如果设为 2,则表示该模块至少要被两个不同的 chunk 引用才会被提取出来。 示例:
module.exports = {
    //...其他配置
    optimization: {
        splitChunks: {
            chunks: 'all',
            minChunks: 2
        }
    }
};
  1. maxAsyncRequests 这个参数限制了在按需加载时,同时加载的最大请求数。默认值是 6。减少这个值可以避免在某些情况下一次性请求过多的文件,从而优化加载性能。 例如,我们将其设为 4
module.exports = {
    //...其他配置
    optimization: {
        splitChunks: {
            chunks: 'all',
            maxAsyncRequests: 4
        }
    }
};
  1. maxInitialRequestsmaxAsyncRequests 类似,maxInitialRequests 限制了入口 chunk 的最大并行请求数。默认值是 3。在页面首次加载时,入口 chunk 的请求数过多可能会影响加载速度,通过设置这个值可以优化初始加载性能。 例如:
module.exports = {
    //...其他配置
    optimization: {
        splitChunks: {
            chunks: 'all',
            maxInitialRequests: 2
        }
    }
};
  1. cacheGroups cacheGroupsSplitChunksPlugin 中最重要的配置选项之一,它允许我们根据自定义的规则来创建多个缓存组。每个缓存组都有自己独立的配置,如 testpriority 等。
    • test:用于匹配模块路径或名称,只有匹配的模块才会被分到该缓存组。它可以是一个正则表达式、字符串或者函数。
    • priority:缓存组的优先级。当一个模块同时满足多个缓存组的条件时,会根据优先级决定被分到哪个缓存组。数值越大优先级越高。
    • name:缓存组生成的 chunk 的名称。如果不设置,Webpack 会根据模块的路径等信息自动生成名称。

示例:

module.exports = {
    //...其他配置
    optimization: {
        splitChunks: {
            chunks: 'all',
            cacheGroups: {
                vendor: {
                    test: /[\\/]node_modules[\\/]/,
                    name:'vendors',
                    priority: -10
                },
                commons: {
                    name: 'commons',
                    minChunks: 2,
                    priority: -20,
                    reuseExistingChunk: true
                }
            }
        }
    }
};

在上述示例中,vendor 缓存组用于匹配 node_modules 中的模块,并将其提取到名为 vendors 的 chunk 中。commons 缓存组用于提取至少被两个 chunk 引用的公共模块,名称为 commons,并且 reuseExistingChunk 设置为 true,表示如果该模块已经在其他 chunk 中存在,则不会重复提取。

实际应用场景

  1. 提取第三方库 在大多数项目中,都会使用大量的第三方库,如 React、Vue、lodash 等。这些库通常体积较大,并且在多个页面或模块中会被重复引用。通过 SplitChunksPlugin,我们可以将这些第三方库提取到单独的 chunk 中,实现缓存和复用。 示例配置:
module.exports = {
    //...其他配置
    optimization: {
        splitChunks: {
            chunks: 'all',
            cacheGroups: {
                vendor: {
                    test: /[\\/]node_modules[\\/]/,
                    name:'vendors',
                    priority: -10
                }
            }
        }
    }
};

假设项目中使用了 React 和 lodash,打包后会将这两个库以及其他来自 node_modules 的模块都提取到 vendors.js 文件中。这样,在页面加载时,只要 vendors.js 被缓存,后续页面再次加载时就无需重复下载这些第三方库,大大提高了加载速度。

  1. 提取公共业务代码 除了第三方库,项目中往往还存在一些公共的业务代码,如工具函数、通用组件等。这些代码也可以通过 SplitChunksPlugin 提取出来,减少每个页面或模块的代码体积。 例如,项目中有一个 utils 文件夹,里面存放了一些通用的工具函数,并且在多个页面中被引用。我们可以这样配置:
module.exports = {
    //...其他配置
    optimization: {
        splitChunks: {
            chunks: 'all',
            cacheGroups: {
                commons: {
                    test: /[\\/]src[\\/]utils[\\/]/,
                    name: 'commons',
                    minChunks: 2
                }
            }
        }
    }
};

这样,src/utils 目录下被至少两个 chunk 引用的模块就会被提取到 commons.js 中。

  1. 按路由分割代码 在单页应用(SPA)中,通常会根据路由来加载不同的页面组件。通过 SplitChunksPlugin,我们可以按照路由来分割代码,实现按需加载。 假设项目使用 React Router,页面结构如下:
src/
├── pages/
│   ├── Home/
│   │   ├── Home.js
│   │   └── Home.css
│   ├── About/
│   │   ├── About.js
│   │   └── About.css
│   └── Contact/
│       ├── Contact.js
│       └── Contact.css
├── App.js
└── index.js

配置如下:

module.exports = {
    //...其他配置
    optimization: {
        splitChunks: {
            chunks: 'all',
            cacheGroups: {
                page: {
                    name: ({ module }) => {
                        const match = module.context.match(/[\\/]src[\\/]pages[\\/](.*?)([\\/]|$)/);
                        return match? `page-${match[1]}` : undefined;
                    },
                    chunks: 'initial',
                    minChunks: 1
                }
            }
        }
    }
};

在上述配置中,page 缓存组根据模块路径生成 chunk 名称,将每个页面的代码分割成单独的文件。例如,Home 页面的代码会被分割到 page - Home.js 中。当用户访问相应路由时,才会加载对应的页面代码,提高了应用的加载性能。

优化策略

  1. 合理设置 minSizemaxSize minSize 设置过小可能会导致生成过多的小 chunk,增加请求数量;设置过大则可能无法充分提取公共代码。maxSize 的设置也需要根据项目实际情况来调整。如果项目主要面向移动端,网络环境相对较差,那么可以适当减小 maxSize,以减少每个请求的大小。 例如,对于移动端项目,我们可以将 minSize 设为 5000(5KB),maxSize 设为 15000(15KB):
module.exports = {
    //...其他配置
    optimization: {
        splitChunks: {
            chunks: 'all',
            minSize: 5000,
            maxSize: 15000
        }
    }
};
  1. 调整 cacheGroups 的优先级 当多个缓存组的 test 规则可能匹配到同一个模块时,优先级就显得尤为重要。确保重要的缓存组(如提取第三方库的缓存组)具有较高的优先级,这样可以避免模块被错误地分到其他缓存组。 例如,我们将 vendor 缓存组的优先级设为 10commons 缓存组的优先级设为 5
module.exports = {
    //...其他配置
    optimization: {
        splitChunks: {
            chunks: 'all',
            cacheGroups: {
                vendor: {
                    test: /[\\/]node_modules[\\/]/,
                    name:'vendors',
                    priority: 10
                },
                commons: {
                    name: 'commons',
                    minChunks: 2,
                    priority: 5
                }
            }
        }
    }
};
  1. 使用 reuseExistingChunkreuseExistingChunk 设置为 true 可以避免重复提取已经存在于其他 chunk 中的模块。这在提取公共代码时非常有用,可以进一步减小打包后的文件体积。
module.exports = {
    //...其他配置
    optimization: {
        splitChunks: {
            chunks: 'all',
            cacheGroups: {
                commons: {
                    name: 'commons',
                    minChunks: 2,
                    reuseExistingChunk: true
                }
            }
        }
    }
};
  1. 分析打包结果 Webpack 提供了一些工具来分析打包结果,如 webpack - bundle - analyzer。通过分析打包后的文件大小、模块依赖关系等信息,我们可以进一步优化 SplitChunksPlugin 的配置。 安装 webpack - bundle - analyzer
npm install --save - dev webpack - bundle - analyzer

webpack.config.js 中添加如下配置:

const BundleAnalyzerPlugin = require('webpack - bundle - analyzer').BundleAnalyzerPlugin;

module.exports = {
    //...其他配置
    plugins: [
        new BundleAnalyzerPlugin()
    ]
};

运行打包命令后,会打开一个浏览器窗口,展示打包后的模块大小、依赖关系等详细信息。根据这些信息,我们可以调整 SplitChunksPlugin 的配置参数,如 minSizecacheGroupstest 规则等,以达到更好的代码分割效果。

注意事项

  1. 避免过度分割 虽然代码分割可以减小文件体积,但过度分割会导致请求数量过多,增加网络开销。在设置 minSizemaxSize 等参数时,要综合考虑项目的实际情况和目标用户群体的网络环境。
  2. 模块依赖顺序 在提取公共模块时,要注意模块之间的依赖关系。如果一个模块 A 依赖于另一个模块 B,并且模块 B 被提取到公共 chunk 中,那么模块 A 也应该被正确地处理,以确保依赖关系的正确性。
  3. 缓存问题 当对代码进行分割后,要注意缓存的设置。对于提取出来的第三方库和公共代码 chunk,应该设置较长的缓存时间,以充分利用缓存提高加载速度。但在代码更新时,要确保缓存能够正确地失效,避免用户加载到旧的代码。

通过合理配置 SplitChunksPlugin,我们可以有效地优化前端项目的打包结果,提高页面加载速度和用户体验。在实际项目中,需要根据项目的特点和需求,不断调整配置参数,以达到最佳的优化效果。同时,结合其他 Webpack 插件和工具,如 webpack - bundle - analyzer,可以更好地分析和优化打包结果。希望本文介绍的内容能够帮助你在前端开发中熟练运用 SplitChunksPlugin,打造高性能的前端应用。