Auto-load a chunk in entry point

Viewed 1411

Webpack configuration is a part of Vue CLI setup (can be checked with vue inspect). This is a relevant part of it:

  entry: {
    foo: [
      '.../src/foo.js'
    ],
    barWidget: [
      '.../src/barWidget.js'
    ],
  },
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendors: {
          name: 'chunk-vendors',
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          chunks: 'initial'
        },
        common: {
          name: 'chunk-common',
          minChunks: 2,
          priority: -20,
          chunks: 'initial',
          reuseExistingChunk: true
        }
      }
    },
    ...

And HTML output is:

<script type="text/javascript" src="/assets/js/chunk-vendors.[HASH].js"></script>
<script type="text/javascript" src="/assets/js/foo.[HASH].js"></script>

and

<script type="text/javascript" src="/assets/js/chunk-vendors.[HASH].js"></script>
<script type="text/javascript" src="/assets/js/barWidget.[HASH].js"></script>

There's no problem for foo to have as many script tags as needed, but barWidget is widget entry point that is supposed to be loaded at once with no initial chunk dependencies. My intention is to make barWidget be loaded with a single line of code (hash will likely be disabled for this purpose):

<script type="text/javascript" src="/assets/js/barWidget.js"></script>

But in its current state it fails to load if chunk-vendors is omitted.

I'd prefer to keep vendors and common chunks as they are because they are splitted in a reasonable way and can be reused on client side between entry points, but I need barWidget to auto-load a chunk it depends on. A less preferable way would be to disable chunks but for barWidget only, chunk splitting in other entry points should remain unchanged.

A way to reproduce it is basically a new Vue CLI project with 2 entry points added, or any Webpack project with similarly configured splitting.

Here is the project (listed for completeness):

package.json

{
  "name": "foobar",
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build"
  },
  "dependencies": {
    "core-js": "^3.6.5",
    "vue": "^3.0.0"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "@vue/compiler-sfc": "^3.0.0"
  }
}

vue.config.js

module.exports = {
  pages: {
    foo: {
      entry: 'src/foo.js',
      template: 'public/foo.html',
      filename: 'foo.html'
    },
    barWidget: {
      entry: 'src/barWidget.js',
      template: 'public/barWidget.html',
      filename: 'barWidget.html'
    },
  },
};

public/foo.html

public/barWidget.html

<!DOCTYPE html>
<html>
  <body>
    <div id="app"></div>
  </body>
</html>

src/foo.js

import { createApp } from 'vue'
import Foo from './Foo.vue'

createApp(Foo).mount('#app')

Foo.vue

<template>
  <HelloWorld msg="Foo"/>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  components: {
    HelloWorld
  }
}
</script>

src/barWidget.js

import { createApp } from 'vue'
import BarWidget from './BarWidget.vue'

createApp(BarWidget).mount('#app')

BarWidget.vue

<template>
  <HelloWorld msg="Bar widget"/>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  components: {
    HelloWorld
  }
}
</script>
  • Can barWidget be forced to automatically load chunk-vendors.[HASH].js by means of Webpack, without loading it explicitly in the place where barWidget.[HASH].js is being used?

  • Can barWidget entry point be forced to not use other initial chunks (chunk-vendors, etc) and output self-sufficient barWidget.js bundle, without affecting the way splitting works in other entry points?

  • Are there other options for the described scenario?

2 Answers

I think that what you want is what is described in this webpack issue reply

the reply uses a function to exclude the dependencies of a specific entrypoint from being included in a chunk:

  optimization: {
    splitChunks: {
      cacheGroups: {
          vendors: {
            // ... your current config, just change the chunks property            

            // Exclude pre-main dependencies going into vendors, as doing so
            // will result in webpack only loading pre-main once vendors loaded.
            // But pre-main is the one loading vendors.
            // Currently undocument feature:  https://github.com/webpack/webpack/pull/6791
            chunks: chunk => chunk.name !== "barWidget"
          }
      }
    }
  },

to do this with vue should be just a matter of changing the webpack config in the vue.config.js file like this:

module.exports = {
  configureWebpack: config => {
     config.optimization.splitChunks.cacheGroups.vendors.chunks = 
       chunk => chunk.name !== "barWidget";
  }
}

I haven't tried this but I think it should work as is or with some minimal tweaks

You can use a function to filter the chunks. See this Webpack issue

vue.confi.js

module.exports = {
  pages: {
    foo: {
      entry: 'src/foo.js',
      template: 'public/foo.html',
      filename: 'foo.html'
    },
    barWidget: {
      entry: 'src/barWidget.js',
      template: 'public/barWidget.html',
      filename: 'barWidget.html',
      chunks: ['barWidget']
    },
  },
  chainWebpack: config => {
    if (process.env.NODE_ENV !== 'test') {
      config.optimization.splitChunks({
        cacheGroups: {
          defaultVendors: {
            name: `chunk-vendors`,
            test: /[\\/]node_modules[\\/]/,
            priority: -10,
            chunks: chunk => chunk.name !== "barWidget"
          },
          common: {
            name: `chunk-common`,
            minChunks: 2,
            priority: -20,
            chunks: 'initial',
            reuseExistingChunk: true
          }
        }
      })
    }
  }
}

Note the pages.barWidget.chunks - it is needed because otherwise HtmlWebpackPlugin includes vendors chunk into barWidget.html even it is not needed at all...

Result: Result

Related