Одной из проблем, которую придется решать при написании Server Side rendering приложения — это работа с метатегами, которые должны быть у каждой страницы, которые помогают при индексации их поисковыми системами.
Начиная гуглить, первое решение, к которому приведут Вас, скорее всего React Helmet.
Одно из преимуществ библиотеки, что ее в некотором роде можно считать изоморфной и может прекрасно использована как на стороне клиента, так и на стороне сервера.
class Page extends Component {
render() {
return (
<div>
<Helmet>
<title>Turbo Todo</title>
<meta name="theme-color" content="#008f68" />
</Helmet>
{/* ... */}
</div>
);
}
}
app.get('/*', (req, res) => {
const html = renderToString(<App />);
const helmet = Helmet.renderStatic();
res.send(`
<!doctype html>
<html ${helmet.htmlAttributes.toString()}>
<head>
${helmet.title.toString()}
${helmet.meta.toString()}
</head>
<body ${helmet.bodyAttributes.toString()}>
<div id="app">${html}</div>
</body>
</html>
`);
});
app.get('/*', async (req, res) => {
// ....
await anyAsyncAction();
//....
const helmet = Helmet.renderStatic();
// ...
});
import { Helmet, HelmetProvider } from 'react-helmet-async';
app.get('/*', async (req, res) => {?
// ... code may content any async actions
const helmetContext = {};
const app = (
<HelmetProvider context={helmetContext}>
<App/>
</HelmetProvider>
);
// ...code may content any async actions
const html = renderToString(app);
const { helmet } = helmetContext;
// ...code may content any async actions
});
app.get('/*', async (req, res) => {?
const helmetContext = {};
let app = (
<HelmetProvider context={helmetContext}>
<App/>
</HelmetProvider>
);
// do a first pass render so that react-helmet-async
// can see what meta tags to render
ReactDOMServer.renderToString(app);
const { helmet } = helmetContext;
response.write(`
<html>
<head>
${helmet.title.toString()}?
${helmet.meta.toString()}
</head>
<body>
`);
const stream = ReactDOMServer.renderToNodeStream(app);
stream.pipe(response, { end: false });
stream.on('end', () => response.end('</body></html>'));
});
class ProductPage extends React.Component {
static createMetatags(store, request){
const item = selectItem(store, request.params.product_id);
return []
.concat({property: 'og:description', content: item.desc})
.concat({property: 'og:title', content: item.title})
}
static loadData(store, request){
// extract external data for SSR and return Promise
}
// the rest of component
}
app.get('/*', async (req, res) => {??
const store = createStore();
const matchedRoutes = matchRoutes(routes, request.path);
// load app state
await Promise.all(
matchedRoutes.reduce((promises, { route }) => {
return route.component.loadData ? promises.concat(route.component.loadData(store, req)) : promises;
}, [])
);
// to get metatags
const metaTags = matchedRoutes.reduce((tags, {route}) => {
return route.component.createMetatags ? tags.concat(route.component.createMetatags(store, req)): tags
});
res.write(`?
<html>?
<head>?
${ReactDOMServer.renderToString(() => metaTags.map(tag => <meta {...tag}/>) )}??
</head>?
<body>?
`);?
const stream = ReactDOMServer.renderToNodeStream(app);?
stream.pipe(response, { end: false });?
stream.on('end', () => response.end('</body></html>'));?
});
К сожалению, не доступен сервер mySQL