Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Webpack 4 and react loadable does not seems to create correct chunk for server side rendering

Tags:

I am trying to create an ssr react app with lazy loading import. Everything works fine except it does not fetch all the required chunks.

I am also wondering if this is related to a dynamic component, which is base on server response

Edit - It actually renders all the required chunks but it wipes out the whole thing when client-side takes over and renders again

Since it re-renders all it slows down by a lot.

enter image description here

The parser happens on server-side and when client-side takes over it fetches more server.js

 const history = createHistory({
    initialEntries: [urlPath],
  })
  // const history = createHistory()
  const store = configureStore(history, {
    location: {
        ...
    },

  })

  const context = {}
  const htmlRoot = (
    <Provider store={store}>
      <StaticRouter location={urlPath} context={context}>
        <AppRoot />
      </StaticRouter>
    </Provider>
  )

  // pre fetching data from api
  store
    .runSaga(rootSaga)
    .done.then(() => {

        const RTS = renderToString(htmlRoot) + printDrainHydrateMarks()
        const head = Helmet.renderStatic() 
        console.log(printDrainHydrateMarks())

        res.status(code).send(renderDom(RTS, port, host, storeState, head))
      }
    })
    .catch(e => {
      console.log(e.message)
      res.status(500).send(e.message)
    })

  renderToString(htmlRoot)
  console.log(printDrainHydrateMarks())

  store.close()
} else {
  res.status(500).send(_err)
}

Prod Server

Loadable.preloadAll().then(() => {
  app.listen(PROD_PORT, (error) => {

  })
});

Client side

Loadable.preloadReady().then(() => {
    hydrate(
      <Provider store={store}>
        <ConnectedRouter history={history}>
          <AppRoot />
        </ConnectedRouter>
      </Provider>,
      domRoot,
    )
  })

Split Chunks setup

    styles: {
      name: 'styles',
      test: /\.css$/,
      chunks: 'all',
      enforce: true
    },

Any opinion or advice is welcome please

Someone suggested to try with window.onload = () => { but this approach seems slow down as well.

like image 952
fiddlest Avatar asked Sep 06 '18 15:09

fiddlest


1 Answers

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

like image 101
Hadi Ranjbar Avatar answered Sep 20 '22 17:09

Hadi Ranjbar