Even when only a handful of modules are utilized, Webpack still imports a significantly large existing chunk

Currently, I am working on enhancing the splitChunks configuration in a multi-page application that utilizes Webpack 5, with a PHP backend and a Vue frontend.

To optimize the vendor file size, I have started customizing the test function of the vendor cacheGroup to exclude certain libraries.

test(module /* , chunk */) {
  if (module.context) {
    // only include node_modules
    const isNodeModule = module.context.includes('node_modules');
    // excluding specific node_modules
    const isToBundleSeparately = [
      'marked', // used in one component only
      'braintree-web', // for payment page
      'bootstrap/js',
    ].some(str => module.context.includes(str));
    if (isNodeModule && !isToBundleSeparately) {
      return true;
    }
  }
  return false;
},

This approach ensures that libraries not required in all pages are imported only when necessary by components linked via dynamic imports, which are then extracted into separate chunks.

While this method has been effective thus far, I recently encountered an unusual behavior involving one particular chunk and library (BootstrapVue).

When I add 'bootstrap-vue' to the list of excluded libraries, I notice that two components from the library, BTab and BTabs, get bundled into a significantly large chunk along with the entire code for the payment page and all libraries utilized on that page.

https://i.sstatic.net/PnbXu.png

The identified file starts with "init-initPaymentGateway", as shown in the screenshot.

Subsequently, all pages dependent on those two BootstrapVue components load this oversized file, including the product page and other pages requiring only these small BootstrapVue components.

Here's an example showcasing the chunk import in the product page:

https://i.sstatic.net/3Mz4y.png

Considering my current configuration, I expected these two components to be allocated into a separate chunk or duplicated if too small. Strangely enough, while Webpack Bundle Analyzer displays very small files and duplicated library files, this behavior does not extend to these specific components. For instance, the BootstrapVue BAlert component, similar in size, gets duplicated across various components.

I suspect the issue arises from these small components, although setting minSize to 0 (or 10) should ideally eliminate any minimum size restrictions for chunk creation.

Included below are the imports within the payment page:

import { BTab, BTabs } from 'bootstrap-vue';
import dataCollector from 'braintree-web/data-collector';

(Which consequently leads to inner components importing further files from braintree-web).

Additionally, the following snippet illustrates the import in a product page component:

import { BTabs, BTab } from 'bootstrap-vue';

Finally, here's the full splitChunks configuration provided (with irrelevant excluded libraries omitted from the test function):

splitChunks: {
  chunks: 'all',
  minSize: 10,
  minChunks: 1,
  maxAsyncRequests: 30,
  maxInitialRequests: 30,
  enforceSizeThreshold: 50000,

  name(module, chunks /* , cacheGroupKey */) {
    const allChunksNames = chunks
      .filter(item => item.name !== null)
      .map(item => item.name.replace('View', ''))
      .join('~');
    return allChunksNames.slice(0, 30);
  },
  cacheGroups: {
    default: {
      minChunks: 2,
        priority: -20,
        reuseExistingChunk: true,
      },
      defaultVendors: {
        name: 'vendor',
        priority: -10,
        chunks: 'all',
        minChunks: 1,
        reuseExistingChunk: true,
        test(module /* , chunk */) {
          if (module.context) {
            // only include node_modules
            const isNodeModule = module.context.includes('node_modules');
            // excluding specific node_modules
            const isToBundleSeparately = [
              'marked', // used in one component only
              'bootstrap-vue',
              'braintree-web',
              'bootstrap/js',
            ].some(str => module.context.includes(str));
            if (isNodeModule && !isToBundleSeparately) {
              return true;
            }
          }
        return false;
      },
    },
  },
},

To rectify the issue, I opted to remove 'bootstrap-vue' from the exclusion list in the test function. However, I am keen on improving my configuration or understanding the rationale behind this peculiar behavior.

Answer №1

After encountering a naming collision issue while attempting to address another problem, I made modifications to the name function which effectively resolved the issue. The conflict stemmed from the slice function causing two modules with the same name to be grouped together in one chunk.

The Webpack documentation explains this scenario: https://webpack.js.org/plugins/split-chunks-plugin/#splitchunksminsize, but the concept was not entirely clear to me until I encountered it firsthand:

Warning

If different split chunks are assigned identical names, all vendor modules will end up in a single shared chunk, potentially resulting in more code being downloaded.

To resolve this issue, I simply extended the length of the name by changing the final line from

return allChunksNames.slice(0, 30);

to

return allChunksNames.slice(0, 40);

Despite this fix, I still felt concerned about potential future naming clashes. Removing the slice portion altogether prompted a Webpack error due to a name that was deemed too lengthy.

Ultimately, the permanent solution involved deleting the name function and allowing Webpack to assign names automatically.


name(module, chunks /* , cacheGroupKey */) {
  const allChunksNames = chunks
    .filter(item => item.name !== null)
    .map(item => item.name.replace('View', ''))
    .join('~');
  return allChunksNames.slice(0, 40);
}

Although the resulting names may be less intuitive, I can utilize the Webpack Bundle Analyzer plugin to discern the contents of each chunk effectively.

Below is the chunk specifically containing the two modules necessary for the product page:

https://i.sstatic.net/Rm1zo.png

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Are routes in Next.js easy for search engines to crawl?

Currently, I am developing a movie application using NextJS to enhance my skills. The homepage features each movie's title and a brief description. When a user clicks on a movie, they are taken to a dedicated page that showcases more details about the ...

Is there a more efficient alternative to the sluggish scroll event?

Currently, I have set up a scroll event that tracks the user's position on the page and updates the navigation styling based on which section they are viewing. However, the calculation I'm using during scrolling is quite resource-intensive and ca ...

Skipping certain key-value pairs during the conversion from JSON to Excel Worksheet using the XLSX library in JavaScript

I have a set of objects in JSON format within my JavaScript code and I am looking to transform this data into an Excel worksheet. Within the JSON structure, there are certain key-value pairs that I do not wish to include in the Excel output. For instance, ...

Issue with Jquery similar to javascript createElement

I am attempting to replicate the code below using jQuery: var newElem = document.createElement("div"); newElem.innerHTML = "DynaColumn"; newElem.className = "ui-state-default ui-corner-all"; return newElem; This is the jQ ...

Toggle the div's visibility to fade in and out once more

Apologies if this is a simple issue to resolve. My goal is to create a div that, when selected, will be replaced by another div with the option to switch back to the original div when "back" is clicked. This is my current progress: $(document).ready( ...

Plane is constantly cloaked in darkness

I'm having trouble adding a texture to my plane that repeats both horizontally and vertically. Every time I try to apply the texture, it shows up as black. I've attempted to add some lights to the scene, but the issue persists without any errors. ...

Chrome compatibility problem with scroll spy feature in Bootstrap 3

Having trouble with scroll spy for boosters using the body method " data-spy="scroll". It seems to be working for some browsers like Edge and Google Chrome, but after multiple attempts, it still doesn't work for me. Even after asking friends to test i ...

Break down Angular modules into smaller parts with the help of webpack

As I work on breaking down a huge Angular project with numerous components, I'm faced with the challenge of dealing with this large module that ideally shouldn't be there in the first place. Unfortunately, due to the current stage of the project, ...

Is there a way to fetch the version of my npm application without pulling in additional package.json details?

I am working on a Single Page Application (SPA) using React and built with Webpack as part of create-react-app. In my application, I display the version number fetched from package.json like this: import npmInfo from 'package.json' ... <div c ...

Issue with Laravel Nova's Tool.vue: Arrow functions are not functioning properly in computed properties

I am facing an issue with a computed property that is not functioning correctly. After referring to the Vue 2.x documentation, here is the code I have: <template> <div> <button :disabled="isDisabled">Import configurator data ...

Maintain the dropdown selection while the table is being updated

My table updates every few seconds and each row has a dropdown menu that allows users to take actions on that row. However, the problem is that when new data is received from the database, the dropdown menu closes if it was open. This can be frustrating f ...

Ways to recycle the table feature in angular2?

I am new to Angular2 framework and I am looking to efficiently reuse a single table component across my entire application. However, I am encountering challenges when it comes to displaying arrays in the table rows. How can I iterate through any type of ar ...

Unable to retrieve HTTP call response during debugging, although it is visible in the browser

When I send an HTTP request to create a record, I am able to see the added record id in the Network section of browsers like Chrome and Firefox. However, when I try to debug the code and retrieve the same id value, I encounter difficulties. I have tried us ...

Issues with hover functionality in React Material Design Icons have been identified

I'm facing an issue with the mdi-react icons where the hovering behavior is inconsistent. It seems to work sometimes and other times it doesn't. import MagnifyPlusOutline from "mdi-react/MagnifyPlusOutlineIcon"; import MagnifyMinusOutli ...

Obtain access to global.window.localStorage within getServerSideProps

I have a component that receives props with data and renders the data. In my functionality within the getServerSideProps, I am attempting to retrieve data from localStorage. However, due to window being undefined, I am unable to do so. I have tried using ...

Dynamic manipulation of classes based on user attributes

One thing I've noticed is that certain WordPress themes, like 'Thematic', include user-specific classes in the body element to avoid using CSS browser hacks. For example: wordpress y2010 m02 d26 h05 home singular slug-home page pageid-94 pa ...

Exploring the possibilities of updating the version dynamically in package.json file

Upon receiving version 1.0.1 (server_version) from the server I make sure process.env.version matches server_version If they are the same, I update process.env.version to server_version. But unfortunately, this process cannot be done from the client side. ...

What is the best way to utilize Gulp and Browserify for my JavaScript application?

Looking to modernize my app with a new build system and I could use some guidance. It seems like I need to shift my approach in a few ways. Here's the current structure of my app: /src /components /Base /App.jsx /Pages.jsx /. ...

The Meteor Call object stands apart from the Meteor Method object that was received

When I send an object from the client to the server using a Meteor Call and Meteor method, something strange happens. The object is received in the Method but it looks different - nested within the giftList. Meteor Call - JSON.stringify {"personName& ...

Exploring Ember Octane (version 3.22 and above): benefits of using {{on 'click' this.function}} over traditional onclick={{this.function}} method

When working with Ember Octane, there are two different ways to attach a function to an event in an hbs file. The first way is the EmberJS approach: {{on 'click' this.function}} Alternatively, you can use the classic HTML method: onclick={{this ...