This solution is mainly to solve the problems of front-end SPA (single page application), slow rendering of the first screen and long white screen time.

Implementation method

Static pages in your application are compiled using webpack’s prerender-spa-plugin and output to the corresponding index directory.

Prerender-spa-plugin describes the principles of the prerender-SPa-plugin

Prerender-spa-plugin leverages the page crawl capabilities of Puppeteer. Puppeteer is Google Chrome’s official Headless Chrome tool. Puppeteer is a Node library that provides an advanced API to control the Headless Chrome on the DevTools protocol. The prerender-SPa-plugin works by starting a local Puppeteer service at the end of the Webpack build phase, accessing the pre-rendered route, exporting the rendered pages in the Puppeteer to an HTML file, and creating a directory for the route.

Configure prerender-spa-plugin in create-react-app

Create-react-app configuration with no eject:

Add the config-overrides. Js configuration file to the root directory of the project and configure it as follows:

const PrerenderSpaPlugin = require('prerender-spa-plugin');
const path = require('path');

module.exports = (config, env) = > {
  if (env === 'production') {
    config.plugins = config.plugins.concat([
      new PrerenderSpaPlugin({
        staticDir: path.join(__dirname, 'build'),
        routes: ['/'].renderer: new PrerenderSpaPlugin.PuppeteerRenderer({
          injectProperty: '__PRERENDER_INJECTED'.inject: {
            prerender: true
          },
          Document. dispatchEvent ()
          // document.dispatchEvent(new Event('render-event'))
          renderAfterDocumentEvent: 'custom-render-trigger',})})]); }return config;
};
Copy the code
Create-react-app configuration has been eject:

Modify webpack.config.js in config folder as follows:

plugins: [
  // Prerender plugin
  isEnvProduction && new PrerenderSpaPlugin({
    staticDir: path.join(__dirname, '.. /build'),
    routes: ['/'].renderer: new PrerenderSpaPlugin.PuppeteerRenderer({
      injectProperty: '__PRERENDER_INJECTED'.inject: {
        prerender: true
      },
      renderAfterDocumentEvent: 'custom-render-trigger',})}),... ]Copy the code

Prerende-spa-plugin Refer to the official documents for details

Recommended solutions are displayed on the page

Pure static page, no interface data:

Document.dispatchevent (new Event(‘custom-render-trigger’))

Non-static page with interface data:

Solution a:

Modify the entry file index.js,

// Check if it is a pre-rendered environment
if(window.__PRERENDER_INJECTED && window.__PRERENDER_INJECTED.prerender){
  / / loading components
  import('./skeleton/index.js');
}else{
  // The original entry file
  import('./page.js');
}
Copy the code

Skeleton /index.js prints skeleton or loading components to index.html as follows:

// Create loading container
const container = document.createElement("div");
container.className = 'prerender-loading';
document.body.appendChild(container);

// Render skeleton components or loading components
ReactDOM.render(
  <div style={{padding: '16px'}} >
    <Skeleton/>
  </div>, 
container);
Copy the code

In the home page component, when the data is loaded, call:

// Remove loading or skeleton components
document.querySelector('.prerender-loading').remove();
// Display the home page.Copy the code

Scheme 2:

Before the data of the home page is loaded, the static part is displayed, and the skeleton component is displayed in the dynamic part. The reference code is as follows:

export default function Index() {
  const [data, setData] = useState(null)

  useMount((a)= > {
    document.dispatchEvent(new Event('custom-render-trigger'));
  });

  / / simulation getdata
  useTimeout((a)= >{ setData({... }); },200)

  return (
    <div className='Index'>
      <div>Static UI</div>
      {
        data === null
        ? <Skeleton>Skeleton UI</Skeleton> 
        : <div>Dynamic data UI</div>
      }
    </div>)}Copy the code