reactjswebpacklazy-loadingreact-lazy-load

Webpack and React.lazy not splitting out dynamic import. Why?


I'm obviously quite confused about this but, I have a module FileAttachments which imports (and no one else does) a fair number of other sizeable modules. I'd expect that when I changed that import to a dynamic import with React.lazy (React 18) then webpack (5.72) would separate the code only required by that import into it's own chunk. But it hasn't done anything of the kind. What am I so confused about? I only setup a single entry point but I thought the point of dynamic imports was that I didn't need to break it up that way.

I changed my code to:

import {observer} from "mobx-react-lite"
import React, {useCallback, Suspense} from "react"
....
import {Box, Divider, Link, Typography} from "@mui/material"
// import FileAttachments from "Widgets/FileAttachments"
const FileAttachments = React.lazy(() => import("Widgets/FileAttachments"));

And properly placed the use inside a Suspense component

   <Suspense fallback={<Box>Loading...</Box>}>
        <FileAttachments attachments={pub.manuscript} editable={true} />
    </Suspense>

I've tried this with virtually every variant of options to webpack optimizations. Right now they are:

   optimization: {
    splitChunks: {
      chunks: 'all',
      maxInitialRequests: Infinity,
      minSize: 0,
      cacheGroups: {}
    },
    runtimeChunk: 'single',
    minimizer: [...   //Long list of optimizatons to Terser and CssMinimizer
          ],
    minimize: true
    }

I've also tried all the suggestions about removing proposal-dynamic-import from my babel configuration. Currently, it's as follows for production but I've tried excluding the proposal-dynamic-import from the presets and removing the syntax-dynamic-import and all the combinations.

   presets: [
         '@babel/preset-env',
                {
                    targets: {
                        "browsers": ["last 1 Firefox versions", "last 1 FirefoxAndroid versions", "last 1 edge versions", "last 1 Chrome versions", "last 1 and_chr versions", "last 1 Safari versions", "last 1 ios_saf versions"],
                    },
                    forceAllTransforms: false,
                    useBuiltIns: 'entry',
                    corejs: '3.8',
                    modules: 'auto',
                    bugfixes: true,
                    exclude: ['transform-typeof-symbol'],
                }
            ],
        plugins: [
            ['@babel/plugin-proposal-class-properties', {loose: false}], //, { loose: true }],
            ['@babel/plugin-transform-runtime', {helpers: false}],
            ["@emotion", emotionOptions],
            [
                "babel-plugin-direct-import",
                {
                    "modules": [
                        "@mui/lab",
                        "@mui/system",
                        "@mui/material",
                        "@mui/icons-material"
                    ]
                }
            ],
            '@babel/preset-react',
                {
                    useBuiltIns: true
                }
          ]

Solution

  • Just in case anyone else runs across this issue. The root cause of the problem was that I was using react-rails to mount my root react component and once I dug through the code it uses I discovered it dynamically requires code from your modules (tries to guess where your component is by filename) and this seemed to stump webpack.

    Once I yanked that code out and simply created/mounted my root manually it all worked fine.