Webpack Example: Difference between revisions

From bibbleWiki
Jump to navigation Jump to search
 
(6 intermediate revisions by the same user not shown)
Line 4: Line 4:
For me I divided this up in the following way<br>
For me I divided this up in the following way<br>
[[file:Webpack_structure.png]]
[[file:Webpack_structure.png]]
=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.
<syntaxhighlight lang="js">
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!')
  }
}
</syntaxhighlight>
=Running the build=
So to do the build we launch this from npm. all pretty obvious
<syntaxhighlight lang="js">
    "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",
</syntaxhighlight>
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<br>
[[File:Bundle_Analzer_2024-12-13_12-26-16.png|300px]]<br>
When the build is run, webpack will output the necessary you need to load in the html. <br>
[[File:Webpack output 2024-12-13 12-29-47.png | 500px]]<br>
This was a mistake I made when using webpack. These bundles '''must''' but in the index.html for it to work.<br>
<syntaxhighlight lang="html">
<!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>
..
</syntaxhighlight>
=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
<syntaxhighlight lang="js">
  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,
        },
      },
    },
</syntaxhighlight>
In react, we use Lazy loading, we can name to the bundles with secret code.
<syntaxhighlight lang="ts">
    const HomePage = lazy(() =>
        import(
            /*
            webpackChunkName: "home-page",
            webpackPrefetch: true
          */
            './pages/homePage/index'
        ).then(module => ({
            default: module.HomePage,
        }))
    )
</syntaxhighlight>
=Environment Variables=
We can define environment variables with the define built-in plugin.
<syntaxhighlight lang="js">
      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),
      }),
</syntaxhighlight>
=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
<syntaxhighlight lang="js">
      new CopyWebpackPlugin({
        patterns: [{ from: 'public', to: outputPath }],
      }),
</syntaxhighlight>

Latest revision as of 23:47, 12 December 2024

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 }],
      }),