Last active
March 22, 2020 19:22
-
-
Save imevro/d29ab267c2c18d16b37377ca6c30232b to your computer and use it in GitHub Desktop.
Minimally working version of SSR (react, react-router, react-helmet, styled-components; implying use of react-scripts without eject)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html lang="ru"> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="$helmet-placeholder$"> | |
<meta name="$sc-placeholder$"> | |
<style> | |
.ssr-placeholder { | |
opacity: 0; | |
} | |
</style> | |
</head> | |
<body> | |
<noscript> | |
Почему ты залез на курс по фронтэнду с выключенным джаваскриптом? | |
</noscript> | |
<div id="root"> | |
<span class="ssr-placeholder">{ssrData}</span> | |
</div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const debug = require('debug')('erodionov:server'); | |
const express = require('express'); | |
const path = require('path'); | |
const handleRenderReactApp = require('./reactApp'); | |
const server = express(); | |
const router = express.Router(); | |
const { HOST = '127.0.0.1', PORT = 80 } = process.env; | |
// статикой раздаём `build` | |
server.use(express.static(path.resolve(__dirname, '..', 'build'))); | |
server.use(router); | |
// на любой запрос вызываем функцию handleRenderReactApp | |
router.get('*', handleRenderReactApp); | |
server.listen(PORT, HOST, () => debug(`app started at ${HOST}:${PORT}`)); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// because we export as ESM, not CJS | |
// eslint-disable-next-line import/first | |
import ReactApp from '../src/App'; | |
const path = require('path'); | |
const fs = require('fs'); | |
const React = require('react'); | |
const { StaticRouter } = require('react-router-dom'); | |
const ReactDOMServer = require('react-dom/server'); | |
const { Helmet } = require('react-helmet'); | |
const { ServerStyleSheet } = require('styled-components'); | |
// берём build/index.html и сохраняем весь контент в indexFileContent | |
const indexFile = path.resolve(__dirname, '..', 'build', 'index.html'); | |
const indexFileContent = fs.readFileSync(indexFile, { encoding: 'utf8' }); | |
// плейсхолдер куда будем рендерить данные | |
const ssrPlaceholder = '<span class="ssr-placeholder">{ssrData}</span>'; | |
// плейсхолдер для react-helmet | |
const helmetPlaceholder = '<meta name="$helmet-placeholder$">'; | |
// плейсхолдер для styled-components | |
const scPlaceholder = '<meta name="$sc-placeholder$">'; | |
module.exports = (req, res) => { | |
// https://www.styled-components.com/docs/advanced#server-side-rendering | |
const sheet = new ServerStyleSheet(); | |
// https://reacttraining.com/react-router/web/guides/server-rendering | |
const context = {}; | |
const App = ReactDOMServer.renderToString( | |
sheet.collectStyles( | |
<StaticRouter location={req.url} context={context}> | |
<ReactApp /> | |
</StaticRouter>, | |
), | |
); | |
// https://github.com/nfl/react-helmet#server-usage | |
const helmet = Helmet.renderStatic(); | |
const styleTags = sheet.getStyleTags(); | |
// редиректим с http-кодом 301 | |
if (context.url) { | |
res.redirect(301, context.url); | |
} | |
const helmetData = ` | |
${helmet.title.toString()}${helmet.meta.toString()}${helmet.link.toString()} | |
`; | |
// вставляем данные | |
const content = indexFileContent | |
.replace(helmetPlaceholder, helmetData) | |
.replace(scPlaceholder, styleTags) | |
.replace(ssrPlaceholder, App); | |
res.send(content); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment