Ребята из nfl вылечили одну из болей React js, работу с head. Речь пойдет о библиотеке react-helmet. Она работает как на клиенте, так и на сервере.
В предыдущей статье я писал о бойлер-плейте, в котором уже использутся react-helmet, поэтому возьму его:
git clone https://github.com/BoryaMogila/koa_react_redux.git;
npm install;
npm run-script run-with-build;
Для тех, у кого своя сборка, ставим модуль:
npm install --save react-helmet
Подключаем в своем в компоненте:
import { Component } from 'react'
import Helmet from "react-helmet"
class SomeComponent extends Component {
render(){
return (
<div>
<Helmet
htmlAttributes={{"lang": "en", "amp": undefined}} // amp takes no value
title="My Title"
titleTemplate="MySite.com - %s"
defaultTitle="My Default Title"
base={{"target": "_blank", "href": "http://mysite.com/"}}
meta={[
{"name": "description", "content": "Helmet application"},
{"property": "og:type", "content": "article"}
]}
link={[
{"rel": "canonical", "href": "http://mysite.com/example"},
{"rel": "apple-touch-icon", "href": "http://mysite.com/img/apple-touch-icon-57x57.png"},
{"rel": "apple-touch-icon", "sizes": "72x72", "href": "http://mysite.com/img/apple-touch-icon-72x72.png"}
]}
script={[
{"src": "http://include.com/pathtojs.js", "type": "text/javascript"},
{"type": "application/ld+json", innerHTML: `{ "@context": "http://schema.org" }`}
]}
//Ваш код
</div>
);
}
Helmet можно использовать в компонентах любой степени вложености, при этом свойства, заданные в компоненте ниже уровнем, будут перетирать свойства, заданные в компоненте уровнем выше.
class SomeComponent extends Component {
render(){
return (
<div>
<Helmet
title="My Title"
meta={[
{"name": "description", "content": "Helmet application"}
]}
link={[
{"rel": "apple-touch-icon", "href": "http://mysite.com/img/apple-touch-icon-57x57.png"},
{"rel": "apple-touch-icon", "sizes": "72x72", "href": "http://mysite.com/img/apple-touch-icon-72x72.png"}
]}
base={{"href": "http://mysite.com/"}}
/>
<AnotherComponent />
</div>
)
}
}
class AnotherComponent extends Component {
render(){
return (
<div>
<Helmet
title="Nested Title"
meta={[
{"name": "description", "content": "Nested component"}
]}
link={[
{"rel": "apple-touch-icon", "href": "http://mysite.com/img/apple-touch-icon-180x180.png"}
]}
base={{"href": "http://mysite.com/blog"}}
/>
</div>
)
}
}
В итоге получим:
<head>
<title>Nested Title</title>
<meta name="description" content="Nested component">
<link rel="apple-touch-icon" href="http://mysite.com/img/apple-touch-icon-180x180.png">
<base href="http://mysite.com/blog">
</head>
Для тайтла есть возможность задать шаблон:
<Helmet
defaultTitle="My Site"
titleTemplate="My Site - %s"
/>
<Helmet
title="Nested Title"
/>
Результат:
<head>
<title>My Site - Nested Title</title>
</head>
Создание тега script:
<Helmet
script={[{
"type": "application/ld+json",
"innerHTML": `{
"@context": "http://schema.org",
"@type": "NewsArticle"
}`
}]}
/>
Результат:
<head>
<script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "NewsArticle"
}
</script>
</head>
Создание тега style:
<Helmet
style={[{
"cssText": `
body {
background-color: green;
}
`
}]}
/>
Результат:
<head>
<style>
body {
background-color: green;
}
</style>
</head>
Для получения данных для head на сервере нужно вызвать метод rewind() после ReactDOM.renderToString или ReactDOM.renderToStaticMarkup.
Возвращенный объект head имеет семь возможных параметров:
- htmlAttributes
- title
- base
- meta
- link
- script
- style
Они имеют два метода toComponent() и toString().
Преобразование данных в строку:
let markup = ReactDOM.renderToString(<Handler />);
let head = Helmet.rewind();
const html = `
<!doctype html>
<html ${head.htmlAttributes.toString()}>
<head>
${head.title.toString()}
${head.meta.toString()}
${head.link.toString()}
</head>
<body>
<div id="content">
${markup}
</div>
</body>
</html>`
//ответ сервера
ctx.body = html;
Решение в стиле React:
let markup = ReactDOM.renderToString(<Handler />);
let head = Helmet.rewind();
function HTML () {
const attrs = head.htmlAttributes.toComponent();
return (
<html {...attrs}>
<head>
{head.title.toComponent()}
{head.meta.toComponent()}
{head.link.toComponent()}
</head>
<body>
<div id="content">
// React stuff here
</div>
</body>
</html>
);
}
//ответ сервера
ctx.body = ReactDOM.renderToString(<HTML />);
Готовые рабочие примеры для использования:
P.S. Как всегда рад услышать ваши замечания и дополнения.