简体   繁体   中英

React SSR blinks page

I created a project with React, react-router, @loadable/component.

Now I'm trying to add SSR to this project. I did server side rendering with react-router.

And then I added @loadable/component to import all pages component:

import loadable from '@loadable/component';

const routersConfig = [
  {
    path: '/',
    component: loadable(() => import('./Home')),
    exact: true,
  },
  {
    path: '/2',
    component: loadable(() => import('./Home2')),
    exact: true,
  },
];

Then I added all this parts of code: https://www.smooth-code.com/open-source/loadable-components/docs/server-side-rendering/

And now it works. But It works with the problem: a content blinks while loading.

How I understand the page's loading process:

  1. Browser gets a content generated by SSR (the first query in network tab)
  2. Browser renders a content (with left and top margins )
  3. Browser downloads two enterpoints and vendors from html (app.js, normalizer.js, vendor.js)
  4. Browser executes app.js and normalizer.js. Left and top margins are removed.
  5. App.js starts downloading page's chunk - home.js. In this moment content disappears
  6. When home.js is downloaded, the content appears again.

I shoot a video to illustrate this process. (I'm sorry for quality, stackoverflow forbides files which size is more then 2MB ). I'm throttling network speed to imagine all page's download process.

在此处输入图片说明

My question is why the content disappears? How to fix it?

My code

server.js

const sheetStyledComponents = new ServerStyleSheet();
  const sheetsJssRegistry = createSheetsRegistry();
  const statsFile = path.resolve(process.cwd(), './build-ssr/dist/loadable-stats.json');

  const extractor = new ChunkExtractor({
    statsFile,
    entrypoints: [
      'app',
      'normalize',
    ],
  });


  try {
    const client = ApolloSSRClient();

    const tree = (
      <ApolloProvider client={client}>
        <ApplyTheme sheetsRegistry={sheetsJssRegistry}>
          <StaticRouter location={req.url}>
            <Home />
          </StaticRouter>
        </ApplyTheme>
      </ApolloProvider>
    );

// there is combination of Apollo graphql, jss, styledComponent functions
    const body = await getMarkupFromTree({
      renderFunction: flow(
        sheetStyledComponents.collectStyles.bind(sheetStyledComponents),
        extractor.collectChunks.bind(extractor),
        renderToString
      ),
      tree,
    });

    const scriptTags = extractor.getScriptTags(); 
    // It isn't used yet
    const linkTags = extractor.getLinkTags(); 

    const styleTags = sheetStyledComponents.getStyleTags();

    const html = (await rawHtml)
      .replace(
        '</head>',
        ` 
          ${styleTags}
          <style type="text/css" id='jss-server-side-styles'>
              ${sheetsJssRegistry.toString()}
          </style>
          <script>
            window.__APOLLO_STATE__ = ${JSON.stringify(client.extract())};
          </script>
          ${scriptTags}
          </head>
        `
      )
      .replace('<div id="app"></div>', `<div id="app">${body}</div>`);


    res.send(html);

index.jsx

const SSRApp = (
  <ApolloProvider client={ApolloClient}>
    <ApplyTheme>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </ApplyTheme>
  </ApolloProvider>
);

loadableReady(() => (
  ReactDOM.hydrate(
    SSRApp,
    document.getElementById('app'),
  )
));

It was my fault.

The hydration version of app contained BrowserRouter -> Switch -> Router -> HomePage

And the SSR version contained only StaticRouter -> HomePage

Because of this, after rendering SSR version, react removed all DOM and created new one with Router.

i changed in server.js. its worked for me

yours maybe (server/index.js or server.js or server/app.js..etc)

import Express from 'express';
import Loadable from 'react-loadable';

// from //


  app.listen(3000, () => {
    console.log('app now listening on port', port);
  });


// to //

  import Loadable from 'react-loadable';

  Loadable.preloadAll().then(() => {
     app.listen(port, () => {
     console.log('app now listening on port', port);
   });
  });

for more config understanding you can see

The first step to rendering the correct content from the server is to make sure that all of your loadable components are already loaded when you go to render them.

To do this, you can use the Loadable.preloadAll method. It returns a promise that will resolve when all your loadable components are ready.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM