Webpack Example

From bibbleWiki
Jump to navigation Jump to search

Introduction

This page is about webpack which I guess is quite old. I wanted to capture what I learned when I used it.

Structure of the Code

For me I divided this up in the following way

webpack.config.js

This essentially handles the merge of the other files. I added to parameters for, which environment and whether to show the BundleAnalyzer report.

import { merge } from 'webpack-merge'

import { commonConfig } from './webpack.config.common.js'
import { devConfig } from './webpack.config.dev.js'
import { prodConfig } from './webpack.config.prod.js'

export default ({ env, withReport }) => {
  console.log(`Building for ${env} environment...`)
  switch (env) {
    case 'development':
      return merge(commonConfig(env, withReport), devConfig(process.env.SSL_KEY_FILE, process.env.SSL_CRT_FILE))
    case 'production':
      return merge(commonConfig(env, withReport), prodConfig)
    default:
      throw new Error('No matching configuration was found!')
  }
}

Running the build

So to do the build we launch this from npm. all pretty obvious

    "start": "webpack serve --config configs/webpack/webpack.config.js --env env=development",
    "dev": "webpack --config configs/webpack/webpack.config.js --env env=development",
    "build": "webpack --config configs/webpack/webpack.config.js --env env=production",
    "report": "webpack --config configs/webpack/webpack.config.js --env env=production --env=withReport",

The report option runs the BundleAnalyzerPlugin which shows the breakdown of the bundle. This tells you what each bundle comprises of. More on that below

When the build is run, webpack will output the necessary you need to load in the html.

This was a mistake I made when using webpack. These bundles must but in the index.html for it to work.

<!doctype html>
<html lang="en">
    <head>
...
        <script defer async src="runtime.js"></script>
        <script defer async src="vendor_react.js"></script>
        <script defer async src="common_app.js"></script>
        <script defer async src="app.js"></script>
    </head>
..

Code Splitting

For production we split the code to allow faster loading of the app. Using the output from the bundle analyzer we can see where we might want to make the splits. For this case we use exceljs which is rater big. For the webpack configure we use splitChunks

  optimization: {
    moduleIds: 'deterministic',
    runtimeChunk: 'single',

    splitChunks: {
      minSize: 17000,
      minRemainingSize: 0,
      minChunks: 1,
      maxAsyncRequests: 30,
      maxInitialRequests: 30,
      automaticNameDelimiter: '_',
      enforceSizeThreshold: 30000,
      cacheGroups: {
        common: {
          test: /[\\/]node_modules[\\/]/,
          priority: -5,
          reuseExistingChunk: true,
          chunks: 'initial',
          name: 'common_app',
          minSize: 0,
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
        // we are opting out of defaultVendors, so rest of the node modules will be part of default cacheGroup
        defaultVendors: false,
        reactPackage: {
          test: /[\\/]node_modules[\\/](react|react-dom|react-router|react-router-dom)[\\/]/,
          name: 'vendor_react',
          chunks: 'all',
          priority: 10,
        },
        exceljsPackage: {
          test: /[\\/]node_modules[\\/](exceljs)[\\/]/,
          name: 'vendor_exceljs',
          chunks: 'all',
          priority: 9,
        },
      },
    },

In react, we use Lazy loading, we can name to the bundles with secret code.

    const HomePage = lazy(() =>
        import(
            /*
            webpackChunkName: "home-page",
            webpackPrefetch: true
          */
            './pages/homePage/index'
        ).then(module => ({
            default: module.HomePage,
        }))
    )

Environment Variables

We can define environment variables with the define built-in plugin.

      new webpack.DefinePlugin({
        'process.env.NODE_ENV': JSON.stringify(env),
        'process.env.REACT_APP_TEST_ENV_VAR': JSON.stringify(process.env.REACT_APP_TEST_ENV_VAR),
      }),

React Specific

Had a bit of trouble with this. So thought I would note it down. For React there is a public directory generated. We need to copy the contents of this to the root directory. Make sure you delete the manifest.json from public if you are using it. Also I found you needed to also delete the %PUBLIC_URL% from the index.html

      new CopyWebpackPlugin({
        patterns: [{ from: 'public', to: outputPath }],
      }),