32
votes

I'm using React + Typescript with Webpack and I'm trying to load some of my react components when they are actually needed.

The issue is that when the chunk is requested via lazy loading I'm getting the following error:

Uncaught Error: Loading chunk 1 failed. (error: http://localhost:58988/1.chunk.js) at HTMLScriptElement.onScriptComplete (bootstrap:114)

This chunk is generated successfully and without any errors, it's just that the path is wrong - http://localhost:58988/1.chunk.js and it should be http://localhost:58988/dist/1.chunk.js

I've also tried using the newest React.lazy to lazy load react components but I'm getting the same issue. So, is there a way to tell the compiler how to resolve these file paths?

Here's some of the code:

MyComponent.tsx

import * as React from "react";
import * as ES5Promise from "promise";

export class MyComponent extends React.Component<{}, {}> {
    constructor(props) {
        super(props);
    }

    private readonly onLoadModuleClickHandler = (): void => {
        import("./Button").then((module) => {
            console.log(module);
        });
    }

    render() {
        return (
            <div>
                <input type="button" value="Load another module" onClick={this.onLoadModuleClickHandler}/>
            </div>
        );
    }
}

tsconfig.json

{
  "compilerOptions": {
    "moduleResolution": "node",
    "noImplicitAny": false,
    "noEmitOnError": true,
    "module": "esnext",
    "removeComments": false,
    "sourceMap": false,
    "target": "es5",
    "jsx": "react",
    "noEmit": true,
    "importHelpers": true,
    "lib": ["dom", "es5", "es2015.promise"]
  },
  "exclude": [
    "node_modules",
    "wwwroot"
  ]
}

webpack.config.js

module.exports = {
    entry: {
        "app": "./src/App.tsx"
    },
    output: {
        path: path.resolve(__dirname, 'wwwroot/dist'),
        filename: "[name].bundle.js",
        chunkFilename: "[name].chunk.js"
    },
    module: {
        rules: [
            {
                test: /\.(ts|tsx)?$/,
                use: "awesome-typescript-loader",
                exclude: /node_modules/
            },
            {
                test: /\.(css|less)?$/,
                use: [{
                    loader: "style-loader"
                }, {
                    loader: "css-loader?modules&localIdentName=[local]--[hash:base64:5]"
                }, {
                    loader: "less-loader"
                }]
            },
        ]
    },
    resolve: {
        extensions: [".js", ".jsx", ".ts", ".tsx", ".css", ".less"]
    }
};
5

5 Answers

33
votes

That happens because output.publicPath by default is /.

Just update output.publicPath to point where you want it to be => /dist/.

15
votes

When redeploying, on rebuilding the app bundles make sure NOT to clean the output folder with previous chunk files, because users that already have the app loaded will try to fetch previous chunk files that will not exist anymore.

Best thing to do is to have an app version tracking (using AJAX and read from db or dynamic config file) and when the app detects a newer version, message the user about it and ask them to reload the page.

5
votes

Let's not over fixate on this. It only happened 5 times. I believe that somebody has excalidraw opened for a while, we push a new version in the meantime and when they get back, they try to load some lazy chunk and it is no longer there.

The way we solve this at Facebook is that we keep the previous chunks on our CDN for a week and we force refresh the app for people if the bundle is more than a week old. This way we don't need to care about very old versions.

The above response is from vjeux.

My plan is to listen for errors in window.onerror, and then prompt the user to refresh the browser.

window.addEventListener('error', e => {
  // prompt user to confirm refresh
  if (/Loading chunk [\d]+ failed/.test(e.message)) {
    window.location.reload();
  }
});
1
votes

In my case window.addEventListener('error'... didn't trigger so I overridden console.error method as follows:

    const origin = console.error;
    console.error = (error) => {
      if (/Loading chunk [\d]+ failed/.test(error.message)) {
        alert('A new version released. Need to relaod the page to apply changes.')
        window.location.reload();
      } else {
        origin(error);
      }
    }
1
votes

Setting the value of output.publicPath in webpack.config.js to / will fix the problem.