Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vendor bundling example #758

Closed
oliverjam opened this issue Sep 21, 2018 · 16 comments
Closed

Vendor bundling example #758

oliverjam opened this issue Sep 21, 2018 · 16 comments

Comments

@oliverjam
Copy link

The vendor bundling example doesn't appear to work as intended. I pulled and ran it and although everything server-rendered fine the output bundles look far too small—React + React DOM can't be 6kb total. Also the client-side code just doesn't run—the click-handler in the example doesn't appear to do anything.

razzle-bug

I reproduced this in my own Razzle project too by copying the same config over. I'm not sure exactly what's going on because I'm far from a webpack expert but I know webpack 4 made a lot of changes to commons chunks etc, so is it possible that broke this?

Also probably unrelated to this but the example code in the README doesn't match the project code (and appear to not even build correctly)

@ghost
Copy link

ghost commented Sep 26, 2018

Same here, doesn't work.

@stale
Copy link

stale bot commented Dec 2, 2018

Hola! So here's the deal, between open source and my day job and life and what not, I have a lot to manage, so I use a GitHub bot to automate a few things here and there. This particular GitHub bot is going to mark this as stale because it has not had recent activity for a while. It will be closed if no further activity occurs in a few days. Do not take this personally--seriously--this is a completely automated action. If this is a mistake, just make a comment, DM me, send a carrier pidgeon, or a smoke signal.

@stale stale bot added the stale label Dec 2, 2018
@oliverjam
Copy link
Author

Vendor bundling is pretty important, so would be good to get this fixed. I might have some time to look at it in the next few weeks

@stale stale bot removed the stale label Dec 2, 2018
@ghost
Copy link

ghost commented Jan 18, 2019

Hi, this example still doesn't work. I can't figure it out why. Vendor bundle doesn't include any react code, same with other libs. Please, can somebody help me with this? Thanks

@mschipperheyn
Copy link
Contributor

Review the documentation on splitChunks and vendor bundling. That should help you out.

@mschipperheyn
Copy link
Contributor

Scratch that. Tried a number of things and couldn't get splitChunks to work. Don't have time to continue on this right now.

@atomic-tang
Copy link

Any updates on this? I really need to get vendor splitting to work as after code splitting with react-loadable, the main bundle size is still 1mb after gzip 😭

@atomic-tang
Copy link

atomic-tang commented Mar 26, 2019

Okay I am not sure if this solves it but I got vendor splitting to work using cacheGroups instead.
For the with-vendor-bundle example, I did the following:

config.optimization = {
   ...config.optimization,
   splitChunks: {
      cacheGroups: {
         chunks: 'all',
         name: 'vendor',
         test: 'vendor',
      }
   }
}

This thread from stackoverflow helped me.

@6thfdwp
Copy link

6thfdwp commented Mar 28, 2019

Followed Webpack site: split chunks example

  optimization: {
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
          name: 'vendor',
          chunks: 'all',
        }
      }
    }
  }

This worked for me. I can see vendor.chunks.js correctly served.

And we do not even need this part in the example (still work when comment it out), not sure how we deal with razzle/polyfills though

      config.entry.vendor = [
        require.resolve('razzle/polyfills'),
        require.resolve('react'),
        require.resolve('react-dom'),
        // ... add any other vendor packages with require.resolve('xxx')
      ];

If @jaredpalmer can help confirm this is the right way, I can submit PR to update the example

@scraton
Copy link

scraton commented Mar 28, 2019

@6thfdwp I can confirm your solution worked for me as well. Thanks for sharing it.

To include razzle/polyfills in your example, I think you should be able to just do this:

vendor: {
  test: /[\\/]node_modules[\\/](razzle|react|react-dom)[\\/]/,
  name: 'vendor',
  chunks: 'all',
}

The way in the docs seems to explicitly state each module to include in the vendor, whereas using test will match based on the directory structure in node_modules. Both ways will work, just different use cases.

Edit: It seems that disables splitting the vendor bundle into chunks, which isn't ideal for production builds (see here). So it will work, but will increase the vendor bundle filesize and prevent it from being chunked up which may not be ideal.

@scraton
Copy link

scraton commented Mar 28, 2019

In order to support vendor bundle splitting in production, it seems you need to use webpack's ManifestPlugin, since razzle's assets-webpack-plugin doesn't seem to report them. I came up with this solution:

// Some additional imports:

const ManifestPlugin = require('webpack-manifest-plugin');
const { extname } = require('path');

// Replace the existing AsetsWebpackPlugin with ManifestPlugin:

      config.plugins = config.plugins.map((plugin) => {
        if (plugin.constructor.name === 'AssetsWebpackPlugin') {
          return new ManifestPlugin({
            fileName: '../assets.json',
            writeToFileEmit: true,
            generate: (seed, files) => (
              files.filter(file => !file.isAsset).reduce((manifest, { name, path }) => {
                // Group files by extension in the manifest
                const ext = extname(path).substr(1);
                manifest[ext] = manifest[ext] || [];
                manifest[ext].push(path);
                return manifest;
              }, seed)
            ),
          });
        }

        return plugin;
      });

// Do vendor splitting:

      config.entry.vendor = [
        require.resolve('razzle/polyfills'),
        require.resolve('react'),
        require.resolve('react-dom'),
      ];
      
      config.optimization = {
        splitChunks: {
          chunks: 'all',
          name: dev ? 'vendor' : false,
        },
      };

You need to specify a name for the splitChunks config in dev, otherwise it will endlessly recompile for some reason.

Then you can import all the assets in your app like so:

  ${assets.css ? assets.css.reduce((styles, asset) => `${styles}<link rel="stylesheet" ref="${asset}">`, '') : ''}

  ${assets.js.reduce((scripts, asset) => `${scripts}<script src="${asset}" defer></script>`, '')}

Perhaps Razzle should migrate to using the ManifestPlugin? I saw it mentioned here, so this may be a good reason to do it.

@6thfdwp
Copy link

6thfdwp commented Mar 31, 2019

@scraton Thanks for sharing and explanation.

So what you suggested here will produce more chunks, while the Webpack site example using

   test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
   name: 'vendor',

will always give one chunk vendor.chunks.js as I saw it, include all packages passing the test, react, react-dom etc.

Is this also the reason why the with-vendor-example not working? I am not sure if I understand this correctly, any explanation will be appreciated, Thanks!

@mschipperheyn
Copy link
Contributor

mschipperheyn commented Apr 4, 2019

I gave it another try to no avail. Using splitChunks leads to endless reloads and no matter what I do, no vendor.js is generated. Tried various .env vars RAZZLE_DEV_BUNDLE_PATH, RAZZLE_BUNDLE_PATH but the manifest always contains a vendor key that contains the standard bundle url

@mschipperheyn
Copy link
Contributor

Well, got it done. No idea, why I didn't before. Here's my working config.
If anyone has any suggestions to optimize this, I'd love to hear it.

if (target === 'web') {

    newConfig.output.filename = dev
        ? 'static/js/[name].js'
        : 'static/js/[name].[hash:8].js';

    newConfig.optimization = {
        ...newConfig.optimization,
        splitChunks: {
            cacheGroups: {
                // Chunk splitting optimiztion
                dte: {
                    test: /[\\/]node_modules[\\/](luxon|@date-io\/luxon)[\\/]/,
                    name: 'dte',
                    reuseExistingChunk: true,
                    priority: -30,
                },
                redux: {
                    test: /[\\/]node_modules[\\/](redux|react-redux)[\\/]/,
                    name: 'redux',
                    reuseExistingChunk: true,
                },
                graphql: {
                    test: /[\\/]node_modules[\\/](graphql|react-apollo|apollo-cache-inmemory|apollo-client|apollo-link|apollo-link-context|apollo-link-http|apollo-link-ws|apollo-utilities)[\\/]/,
                    name: 'apollo',
                    reuseExistingChunk: true,
                },
                form: {
                    test: /[\\/]node_modules[\\/](yup|react-final-form|final-form)[\\/]/,
                    name: 'form',
                    reuseExistingChunk: true,
                    priority: -30,
                },
                vendor: {
                    test: /[\\/]node_modules[\\/](react|react-dom|@lingui|react-router|react-router-dom)[\\/]/,
                    name: 'vendor',
                    reuseExistingChunk: true,
                },
                default: {
                    minChunks: 2,
                    priority: -20,
                    reuseExistingChunk: true,
                },
            },
        },
    };
}

@revskill10
Copy link

@mschipperheyn Could you please send a PR ?

@stale stale bot added the stale label Jun 15, 2019
@fivethreeo
Copy link
Collaborator

Should be fixed in dev but not using vendors but chunkGroups config

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants