score:0

I am using route-centric code splitting with react-universal-component and flush chuck. Sorry for the long code snippet/pseudo code, I have tried my best to make it shorter. :)

First build an array for react router mapping corresponding components

let routeConfig = [
  {path: '/foo', component: 'Foo'},
  {path: '/bar', component: 'Bar'}
];

universal function make sure the component and its children can be correctly imported on both server side and client side. It's smart enough to lazy load the split codes accordingly.

import universal from 'react-universal-component';
const routes = routeConfig.map((item) =>{
  let route = {};
  route.path = item.path;
  route.component = universal(import(`./container/${item.component}`), options);
  return route;
});

Render the router in React Component.

class App extends React.Component {
 render() {
    return (
      <div>
        <Switch>
          {routes.map( route => <Route key={ route.path } { ...route } />)}
        </Switch>
      </div>
);}}

Config webpack define the code splitting names.

  output: {
    filename: '[name].js',
    chunkFilename: '[name].js',
    path: path.resolve(__dirname, '../dist/client'),
    publicPath: '/xxxxxx/'
  },
  plugins: [
    new MiniCssExtractPlugin({
      // Options similar to the same options in webpackOptions.output
      // both options are optional
      filename: '[name].css',
      chunkFilename: '[id].css'
  })],
  optimization: {
    splitChunks: {
      chunks: 'initial',
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/, 
          name: 'vendor' // bundle all the npm module as vendor.js
}}}}

Finally, the server.js will use flush chuck to do the code splitting.

const chunkNames = flushChunkNames();
const {js, styles, cssHash, scripts, stylesheets} = flushChunks(clientStats, {chunkNames});
res.send(`<!doctype html>
  <html>
    <head>
      ${styles}
    </head>
    <body>
      <div id="root">${app}</div>
      ${cssHash}
      ${js}
    </body>
  </html>`);

score:0

You should consider using ReactLoadableSSRAddon, it works better than the plugin provided by ReactLoadable see this link for further info. In my case it made a huge difference !

score:0

if you are using ts-loader try setting:

tsconfig.json

{
  .
  .
  .

  "module" : "esnext",
  "moduleResolution": "Node",
  .
  .
  .
}

score:2

You should use ReactLoadablePlugin to have a list of loadable imports:

new ReactLoadablePlugin({
  filename: './build/react-loadable.json',
})

Using "react loadable capture", you can find out which dynamic components are needed while rendering and you can add their bundles to your header file:

const content = ReactDOMServer.renderToString(
  <Loadable.Capture report={moduleName => modules.push(moduleName)}>
    <Provider store={configureStore()}>
      <StaticRouter location={req.url} context={context}>
        <App />
      </StaticRouter>

    </Provider>
  </Loadable.Capture>
);

  let bundles = getBundles(stats, modules);
  bundles.map((item) => {
      //add js to header
      })

This prevents react loadable from wiping out the content and rerendering it.

To config the Webpack to output right chunk based on dynamically loaded component do this:

 optimization: {
    splitChunks: {
      cacheGroups: {
          default: false,
          vendors: false,
      }
  }
}

This configuration works for me in Webpack v4.

You can find a complete documentation to work with React loadable server-side rendering here:

https://github.com/jamiebuilds/react-loadable


Related Query

More Query from same tag