Ответный шлем выводит пустые строки на стороне сервера
Я использую реагирующий шлем, и на клиенте все хорошо в окне проверки, и теги выводятся правильно. Тем не менее, когда я загружаюсь в производственном режиме, и SSR-пики в тегах не отображаются в источнике, и я не получаю никаких ошибок вообще.
Я также попытался записать тег заголовка 'stringified' и получил:
<title data-react-helmet="true"></title>
Вот некоторый код:
Это один из компонентов страницы, с которого я устанавливаю теги, все 3-страничные компоненты настроены одинаково для этого. (Я упростил функцию рендеринга компонентов и объекта данных, поскольку они довольно большие, и я уверен, что они не виноваты.)
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { Helmet } from 'react-helmet';
// Components
import WorkGrid from 'universal/components/Grid';
import Wrapper from 'universal/components/Wrapper';
import Container from 'universal/components/Container';
import Hero from 'universal/components/Hero';
import PageWrapper from 'universal/components/PageWrapper';
import GridHeader from 'universal/components/GridHeader';
const data = {};
class Work extends PageComponent {
render () {
return (
<PageWrapper ref="root">
<Helmet>
<title>Work</title>
<meta name="description" content="Work Description" />
</Helmet>
<h1>Work Page</h1>
</PageWrapper>
);
}
}
export default connect(state => ({
theme: state.ui.theme
}), { changeTheme }, null, { withRef: true })(Work);
Это часть серверного кода, особенно там, где отключается SSR, и я вызываю Helmet.renderStatic();
// Node Modules
import fs from 'fs';
import {basename, join} from 'path';
// Libraries
import React from 'react';
import {StaticRouter} from 'react-router';
import {renderToString} from 'react-dom/server';
// styled-components
import { ServerStyleSheet, ThemeProvider } from 'styled-components';
import { theme } from '../universal/constants';
// Redux
// import {push} from 'react-router-redux';
import createStore from 'data/redux/createStore.js';
import createHistory from 'history/createMemoryHistory';
import { Provider } from 'react-redux';
// Third Party Scripts
import * as thirdPartyScripts from './thirdPartyScripts.js';
// Helmet
import {Helmet} from 'react-helmet';
function renderApp(url, res, store, assets) {
const PROD = process.env.NODE_ENV === 'production';
const context = {};
const {
manifest,
app,
vendor
} = assets || {};
let state = store.getState();
const stylesheet = new ServerStyleSheet();
const initialState = `window.__INITIAL_STATE__ = ${JSON.stringify(state)}`;
const Layout = PROD ? require( '../../build/prerender.js') : () => {};
const root = PROD && renderToString(
stylesheet.collectStyles(
<Provider store={store}>
<ThemeProvider theme={theme}>
<StaticRouter location={url} context={context}>
<Layout />
</StaticRouter>
</ThemeProvider>
</Provider>
)
);
const styleTags = stylesheet.getStyleTags();
const seo = Helmet.renderStatic();
console.log(seo.title.toString());
const html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
${seo.title.toString()}
${seo.meta.toString()}
${seo.link.toString()}
<link rel="shortcut icon" href="/favicon.ico">
<link rel="icon" sizes="16x16 32x32 64x64" href="/favicon.ico">
<link rel="apple-touch-icon" href="/favicon-57.png">
<link rel="apple-touch-icon" sizes="114x114" href="/favicon-114.png">
<link rel="apple-touch-icon" sizes="72x72" href="/favicon-72.png">
<link rel="apple-touch-icon" sizes="144x144" href="/favicon-144.png">
<link rel="apple-touch-icon" sizes="60x60" href="/favicon-60.png">
<link rel="apple-touch-icon" sizes="120x120" href="/favicon-120.png">
<link rel="apple-touch-icon" sizes="76x76" href="/favicon-76.png">
<link rel="apple-touch-icon" sizes="152x152" href="/favicon-152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/favicon-180.png">
<meta name="msapplication-TileColor" content="#FFFFFF">
<meta name="msapplication-TileImage" content="/favicon-144.png">
<meta name="msapplication-config" content="/browserconfig.xml">
${ styleTags }
${PROD ? '<link rel="stylesheet" href="/static/prerender.css" type="text/css" />' : ''}
<link href="${thirdPartyScripts.googleFont}" rel="stylesheet" type="text/css">
<script>${thirdPartyScripts.googleAnalytics}</script>
</head>
<body>
<script>${initialState}</script>
${PROD ? `<div id="root">${root}</div>` : '<div id="root"></div>'}
${PROD ? `<script>${manifest.text}</script>` : ''}
<script>${thirdPartyScripts.facebookPixel}</script>
<script async src="${thirdPartyScripts.googleAnalyticsSrc}"></script>
${PROD ? `<script src="${vendor.js}"></script>` : ''}
<script src="${PROD ? app.js : './static/app.js'}"></script>
</body>
</html>`;
res.send(html);
}
Кроме того, я использую React Router v4, если это поможет.
1 ответ
Я нашел решение этой проблемы на другой неделе и подумал, что я могу также обновить это, чтобы оно могло помочь кому-либо еще, имеющему эту проблему...
Хорошие новости - это было удивительно просто!
Для меня в любом случае все сводилось к тому, что я разделяю пакеты веб-пакетов для клиента и сервера. Таким образом, с точки зрения непрофессионала, он включал реагирующий шлем дважды, один раз для клиента и один раз для сервера, то есть все состояние, содержащее метатеги в коде на стороне клиента, не существовало в вызове.rewind() на сервере.
Просто добавьте это в файл конфигурации вашего сервера.
externals: ['react-helmet']