Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to share the same instance of jQuery across multiple Webpack packs?

I'm working in a Rails app, migrating from Sprockets to Webpack. Our current JavaScript relies on libraries globally assigned to window. In order to migrate over, I'd like to maintain global assignment until we address it at a later date. I'd like to swap from Sprockets to Webpack without any JavaScript changes.

We have several JavaScript bundles, which have been manually split up for performance:

  • core.js, which contains jQuery and a couple other files. It's a blocking bundle.
  • vendor.js, which contains all other libraries and jQuery plugins. It's a deferred bundle.
  • application.js, which contains our custom application code. Also a deferred bundle.

All the bundles rely on jQuery, and application.js relies on vendor.js. That's fine for Sprockets, but is an issue with Webpack.

How can I keep jQuery in core.js, but exclude it from vendor.js and application.js in Webpack? or another way to ask this is How do I share the same instance of jQuery across multiple webpack bundles? I need to use the same instance because application.js relies on jQuery plugins defined in vendor.js

My config file includes both expose-loader and ProvidePlugin, but these include jQuery in the bundle itself, which is not the same instance. In other words, I'm getting jQuery bundled multiple times (which is fixed with splitChunks()), but I can't guarantee which instance I'm using, therefore can't guarantee the plugin is available.

// Webpacker environment.js config

const { environment } = require('@rails/webpacker');
const path = require('path');
const webpack = require('webpack');

environment.loaders.append('expose', {
  test: require.resolve('jquery'),
  use: [
    {
      loader: 'expose-loader',
      options: 'jQuery'
    },
    {
      loader: 'expose-loader',
      options: '$'
    }
  ]
});

environment.plugins.append(
  'Provide',
  new webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery'
  })
);


module.exports = environment;
like image 366
bholtbholt Avatar asked Nov 06 '25 16:11

bholtbholt


1 Answers

I have had success with splitChunks forcing a single instance for jquery combined with the expose-loader config like you have described.

Here's the key part of the Webpack config I'm using:

environment.splitChunks((config) => {
  return Object.assign({}, config, {
    optimization: {
      splitChunks: {
        chunks: 'all',
        name: true,
        cacheGroups: {
          vendors: {
            test: /[\\/]node_modules[\\/]/,
            priority: -10,
          },
          default: {
            minChunks: 2,
            priority: -20,
            reuseExistingChunk: true,
          },
        },
      },
    },
  })
})

The key here is to understand that Webpack splitChunks replaces the need for manual code splitting. In other words, let go of the need to explicitly create core.js and vendor.js packs; consolidate all your imports as part of the application.js dependency tree. Let Webpack do the code-splitting for you.

Another caveat to mention is that Webpacker's implementation of splitChunks means placing a single javascript_packs_with_chunks_tag in the view so that the shared "runtime" chunk will ensure that modules, like jQuery, aren't duplicated.

I used to split my bundles manually like you in my previous projects with Sprockets. When I had to do my own migration to Webpack, it took awhile for this new approach to sink in. I gave a talk about it which will shed some more light on my answer here: https://youtu.be/fKOq5_2qj54?t=185

like image 121
rossta Avatar answered Nov 09 '25 04:11

rossta



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!