Перейти к контенту

🎉 Material UI v5 is out now! Check out the announcement blog post

Серверная отрисовка

Наиболее распространенный вариант использования для серверной отрисовки - это использовать начальную отрисовку, когда пользователь (или поисковой движок) впервые запрашивает ваше приложение.

Когда сервер получает запрос, он отрисовывает необходимые компоненты в HTML строк, а затем отправляет ее как ответ клиенту. С этого момента клиент берет на себя обязанности по отрисовке.

Material-UI on the server

Material-UI was designed from the ground-up with the constraint of rendering on the server, but it's up to you to make sure it's correctly integrated. It's important to provide the page with the required CSS, otherwise the page will render with just the HTML then wait for the CSS to be injected by the client, causing it to flicker (FOUC). It's important to provide the page with the required CSS, otherwise the page will render with just the HTML then wait for the CSS to be injected by the client, causing it to flicker (FOUC).

  1. Create a fresh, new ServerStyleSheets instance on every request.
  2. Render the React tree with the server-side collector.
  3. Pull the CSS out.
  4. Передать CSS клиенту.

На стороне клиента CSS будет добавлен второй раз перед удалением добавленного сервером CSS.

Настройка

В следующем рецепте мы рассмотрим, как настроить серверную отрисовку.

The theme

Create a theme that will be shared between the client and the server:

theme.js

import { createTheme } from '@material-ui/core/styles';
import red from '@material-ui/core/colors/red';

// Create a theme instance.
const theme = createTheme({
  palette: {
    primary: {
      main: '#556cd6',
    },
    secondary: {
      main: '#19857b',
    },
    error: {
      main: red.A400,
    },
    background: {
      default: '#fff',
    },
  },
});

export default theme;

The server-side

The following is the outline for what the server-side is going to look like. We are going to set up an Express middleware using app.use to handle all requests that come in to the server. If you're unfamiliar with Express or middleware, just know that the handleRender function will be called every time the server receives a request.

server.js

const css = sheets.toString();

  // Send the rendered page back to the client.
function renderFullPage(html, css) {
  /* ... */
}

function handleRender(req, res) {
  /* ... res.send(renderFullPage(html, css));
}

const app = express();

app.use('/build', express.static('build'));

// This is fired every time the server-side receives a request.
*/
}

const app = express();

// Isso é acionado toda vez que o servidor recebe uma solicitação.

Обработка запроса

The first thing that we need to do on every request is create a new ServerStyleSheets.

When rendering, we will wrap App, the root component, inside a StylesProvider and ThemeProvider to make the style configuration and the theme available to all components in the component tree.

The key step in server-side rendering is to render the initial HTML of the component before we send it to the client side. To do this, we use ReactDOMServer.renderToString().

We then get the CSS from the sheets using sheets.toString(). We will see how this is passed along in the renderFullPage function.

const css = sheets.toString();

  // Send the rendered page back to the client.
  res.send(renderFullPage(html, css));
}

const app = express();

app.use('/build', express.static('build'));

// This is fired every time the server-side receives a request.
  const html = ReactDOMServer.renderToString(
    sheets.collect(
      <ThemeProvider theme={theme}>
        <App />
      </ThemeProvider>,
    ),
  );

  // Grab the CSS from the sheets.
  function renderFullPage(html, css) {
  /* ... */
}

const app = express();

// Isso é acionado toda vez que o servidor recebe uma solicitação.
app.use(handleRender);

const port = 3000;
app.listen(port);

Inject Initial Component HTML and CSS

The final step on the server-side is to inject the initial component HTML and CSS into a template to be rendered on the client side.

function renderFullPage(html, css) {
  return `
    <!DOCTYPE html>
    <html>
      <head>
        <title>My page</title>
        <style id="jss-server-side">${css}</style>
      </head>
      <body>
        <div id="root">${html}</div>
      </body>
    </html>
  `;
}

The Client Side

The client side is straightforward. All we need to do is remove the server-side generated CSS. Let's take a look at the client file:

client.js

import React from 'react';
import ReactDOM from 'react-dom';
import { ThemeProvider } from '@material-ui/core/styles';
import App from './App';
import theme from './theme';

function Main() {
  React.useEffect(() => {
    const jssStyles = document.querySelector('#jss-server-side');
    if (jssStyles) {
      jssStyles.parentElement.removeChild(jssStyles);
    }
  }, []);

  return (
    <ThemeProvider theme={theme}>
      <App />
    </ThemeProvider>
  );
}

ReactDOM.hydrate(<Main />, document.querySelector('#root'));

Reference implementations

We host different reference implementations which you can find in the GitHub repository under the /examples folder:

Troubleshooting

Check out the FAQ answer: My App doesn't render correctly on the server.