javascriptreactjswebpackhandsontable

Issue converting standard import statements over to React.lazy() in order to code split a bundle


I want to take the following imports and code:

import {HotTable} from '@handsontable/react';
import {registerAllModules} from 'handsontable/registry';
registerAllModules();

And convert them over to use React.lazy() so that webpack will create a separate bundle for the HandsOnTable library.

If I do this:

const HotTable = React.lazy(() => import('@handsontable/react'));
import {registerAllModules} from 'handsontable/registry';
registerAllModules();

The code will still run, but HandsOnTable is still in the main bundle.

If I try to do something like this:

const HotTable = React.lazy(() => import('@handsontable/react'));
React.lazy(
  () => import('handsontable/registry').then((registerAllModules) => {
    registerAllModules();
    return { registeredModules: true };
  })
);

webpack does split the library out to another bundle, but the loading of the HandsOnTable bundle fails with a content decoding error:

main.90757321a1336791f3c9.bundle.js:2 
GET http://localhost:5000/215.21bf19f9d6a7317b2ad5.bundle.js 
net::ERR_CONTENT_DECODING_FAILED 200 (OK)

UPDATE: The issue seems to be associated with compression-webpack-plugin. If I disable compression, then the page with the HandsonTable component loads successfully.
But I still need to go it to work with compression.


Solution

  • I figured out the issue was tied to this configuration for compression-webpack-plugin:

      new CompressionPlugin({
        test: /\.js$|\.css$|\.html$/,
        threshold: 10240,
        minRatio: 0.8
      })
    

    One of the bundles was below the threshold size (10240) and therefore wasn't being compressed.

    In many places on the web you will see the following code suggested for the express server when using compression-webpack-plugin:

    app.get('*.js', function(req, res, next) {
      req.url = req.url + '.gz';
      res.set('Content-Encoding', 'gzip');
      res.set('Content-Type', 'text/javascript');
      next();
    });
    

    However this code doesn't account for the scenario where a bundle might not have been compressed. I updated this code with a condition to serve up a non-compressed file for when the compressed file isn't found:

      app.get('*.js', function (req, res, next) {
        const pathToGzipFile = req.url + '.gz';
        // Not all files are compressed due to threshold and minRatio settings.
        if (fs.existsSync(path.join(clientDirPath, pathToGzipFile))) {
          res.set('Content-Encoding', 'gzip');
          req.url = req.url + '.gz';
        }
        res.set('Content-Type', 'application/javascript');
        next();
      });
    

    And the error went away.