12

We have a traditional server rendered application (non SPA) where each page is augmented with vuejs

Our existing webpack 3 configuration is

webpack.config.js

var webpack = require('webpack')
var path = require('path')

const ExtractTextPlugin = require('extract-text-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')

module.exports = {
    entry: {
        shared: './shared.js',
        pageA: './pageA.js',
        // pageB: './pageB.js',
        // pageC: './pageC.js',
        // etc
    },

    resolve: {
        alias: { vue: 'vue/dist/vue.esm.js' },
    },

    output: {
        path: path.join(__dirname, './dist'),
        filename: '[name].js',
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                exclude: /node_modules/,
                use: ExtractTextPlugin.extract({
                    use: [
                        {
                            loader: 'css-loader',
                            query: {
                                sourceMap: true,
                            },
                        },
                    ],
                }),
            },
        ],
    },

    plugins: [
        new CleanWebpackPlugin('./dist'),

        new webpack.optimize.CommonsChunkPlugin({
            name: ['shared'],
            minChunks: Infinity,
        }),

        new webpack.optimize.CommonsChunkPlugin({
            name: 'runtime',
        }),

        new ExtractTextPlugin('[name].css'),

        new CopyWebpackPlugin([{ from: 'index.html', to: '.' }]),
    ],
}

shared.js

// import shared dependencies & pollyfills
var vue = require('vue')

// import global site css file
require('./shared.css')

// initialize global defaults
// vue.setDefaults(...)

console.log('shared', { vue })

pageA.js

var vue = require('vue')

// only this page uses axios
var axios = require('axios')

console.log('pageA', { vue, axios })

shared.css

body {
    background-color: aquamarine;
}

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <!-- included on every page-->
    <link rel="stylesheet" href="shared.css">
</head>
<body>
    <!-- included on every page-->
    <script src="runtime.js"></script>
    <script src="shared.js"></script>

    <script src="pageA.js"></script>
</body>
</html>

With this setup

1) runtime.js contains the webpack loader, so any changes to shared.js don't cause pageA.js to be cache busted and vice versa

2) shared.js contains any shared dependencies (in this case vue) as well as any shared global initializion for every page (setting vue defaults etc). It is also the point we import our shared global css file.

3) pageA.js does not contain any dependencies imported in shared.js (vue in this case) but does contain dependicies it imports (axios in this case).

We have been unable to reproduce this setup using the SplitChunksPlugin

1) SplitChunksPlugin doesn't seem to allow an entry point as a split point.

2) All the examples have split out ALL node module dependices into a vendor chunk. This doesn't work for us as we have 100's of pages but only a few import a graphing library or moment etc... We don't want to have this graphing library or moment included in shared.js as it will then be load for all pages.

3) It wasn't clear how to split the runtime into its own file either

SplitChunksPlugin seems to be targeted at SPA's where javascript can be loaded on demand. Is the scenario we are trageting still supported?

Guntram
  • 961
  • 14
  • 19
kimsagro
  • 15,513
  • 17
  • 54
  • 69
  • splitchunksplugin came to fix a problem where people were inserting vendors as entrypoints, which is a bad idea. You must let webpack split everything from node_modules into its own chunk. Splitting everything from node_modules fixes your problem? – PlayMa256 Sep 18 '18 at 11:47

2 Answers2

3

Are you trying to migrate to webpack 4?

I find the optimisation cacheGroups test option works well to be specific about what goes where.

optimization: {
  splitChunks: {
    cacheGroups: {
      shared: {
        test: /node_modules[\\/](?!axios)/,
        name: "shared",
        enforce: true,
        chunks: "all"
      }
    }
  }
}

Will load everything from the node modules (except axios) and should therefore be included as part of your page entry point.

Eliott Robson
  • 960
  • 9
  • 20
  • does it mean that you override default "vendor" cachedGroup - I just try to find suitable terminology for this configuration modification? – Roman Pokrovskij Sep 21 '18 at 13:28
  • You can add your own or override the vendor one if that is taking precedence. The idea is to explain to webpack that it should not bundle specific modules. Thus, when it comes to packaging the entry points it will automatically bundle the ignored ones inline. – Eliott Robson Sep 23 '18 at 09:50
0

If you want webpack to chunk some component, you will need to import it asynchronously from your main entry file. I have been using bundle-loader to do it, then I have:

In my webpack.config.js

optimization: {
  splitChunks: {
    chunks: 'all'
  },
  mergeDuplicateChunks: true,
}
module: {
  rules: [
    {
      test: /\.bundle\.js$/, //yes my output file contains the bundle in its name
      use: {
        loader: 'bundle-loader', options: {lazy: true}
      }
    }
  ]
}

In my entry file.

//this code will replace the line where you are importing this component
let Login;

// this method will go inside your component
componentWillMount() {
    require("bundle-loader!./ui/Login.jsx")((loginFile) => {
        Login = loginFile.default;
        this.setState({ loginLoaded: true });
    });
}

If you don't want to use it, there are more ways of importing your file async.

Cleriston
  • 750
  • 5
  • 11
  • Could you explain what does it mean "to import it (weback?) asynchronously " ? – Roman Pokrovskij Sep 21 '18 at 13:15
  • not the webpack, you import the component you want to be chuncked async. Like in my example I am importing Login when component is mounted. – Cleriston Sep 21 '18 at 14:10
  • you mean webpack will handle require("bundle-loader!./ui/Login.jsx") ? – Roman Pokrovskij Sep 21 '18 at 18:30
  • Sort of. Take a look at: github.com/webpack-contrib/bundle-loader. Also for understanding how webpack handles chunks https://itnext.io/react-router-and-webpack-v4-code-splitting-using-splitchunksplugin-f0a48f110312 – Cleriston Sep 22 '18 at 02:05