Reputation: 6267
I have a webpack main.bundle.js
that weights 287kb
thanks to codesplitting, but my vendor.js
is 5mb
. When the user visits the website for the firs time, he will have to download 5.2mb
, which is too large.
What is the proper way to split the vendor so the user only downloads the packages he needs to run the page, and then webpack prefetches all the remaining packages in the background?
I'm using webpack 4 (I'm waiting for webpack 5 to be supported by Storybook before upgrading. If there is a new way of doing it in W5, please le me know).
Here is my config:
/* eslint-env node */
const path = require("path");
const TerserPlugin = require("terser-webpack-plugin");
const Dotenv = require("dotenv-webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const isProductionMode = (mode) => mode === "production";
module.exports = () => {
const env = require("dotenv").config({ path: __dirname + "/.env" });
const nodeEnv = env.parsed.NODE_ENV;
return {
mode: "development",
entry: "./src/index.tsx",
output: {
path: path.join(__dirname, "./dist"),
filename: "[name].[hash].bundle.js",
publicPath: "/",
},
resolve: {
extensions: [".ts", ".tsx", ".js", "jsx", ".json"],
},
module: {
rules: [
{
test: /\.(ts|js)x?$/,
exclude: /node_modules/,
use: { loader: "babel-loader" },
},
{ test: /\.css$/, use: ["style-loader", "css-loader"] },
{ test: /\.(png|jpg|jpeg|gif)$/, use: ["file-loader"] },
{
test: /\.svg$/,
use: [
{
loader: "babel-loader",
},
{
loader: "react-svg-loader",
options: {
jsx: true,
},
},
],
},
],
},
devServer: {
historyApiFallback: true,
port: 3000,
inline: true,
hot: true,
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
}),
new Dotenv(),
],
optimization: {
minimize: isProductionMode(nodeEnv),
minimizer: isProductionMode(nodeEnv) ? [new TerserPlugin()] : [],
splitChunks: { chunks: "all" },
},
};
};
Upvotes: 4
Views: 7646
Reputation: 2706
This discussion https://github.com/facebook/create-react-app/discussions/9161 contains some configs (Webpack 5 and should no tneed much adjustmenst to work with Webpack 4) that may give you a good default if you want to learn and reuse a single chunking strategy.
If you prefer something simple you can add 1-2 custom cacheGroups
under config.optimization.splitChunks
that can look something like this:
firebase: { // Internal identifier, just any unique name.
test: /@firebase|redux-fire/, // This will match any file path containing @firebase or redux-fire (base and store) which is massive.
name: "firebase", // Add a name to easily spot this chunk among outputs, or remove the `name` property to get a number instead which seems to be best-practice.
priority: 10, // Higher prio = gets processed earlier. Defaults are on below 0.
},
The above is compatible with Webpack 4 and 5 as far as I see: https://v4.webpack.js.org/plugins/split-chunks-plugin/
If you want more control you can also cancel out the default groups:
cacheGroups: {
firebase: {
test: /@firebase|redux-fire/,
name: "firebase",
priority: 20,
},
defaultVendors: false,
default: false,
// Add more groups here
},
Upvotes: 0
Reputation: 109
This helped me in splitting the vendor bundle.
Source: https://gist.github.com/davidgilbertson/c9af3e583f95de03439adced007b47f1
splitChunks: {
chunks: 'all',
enforce: true,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name(module) {
// get the name. E.g. node_modules/packageName/not/this/part.js
// or node_modules/packageName
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
// npm package names are URL-safe, but some servers don't like @ symbols
return `npm.${packageName.replace('@', '')}`;
},
},
},
},
Upvotes: 6