Универсальний (изоморфный) «шлем» для React js или Как удобно работать с head на React js

в 8:11, , рубрики: javascript, node.js, react-helmet js node js javascript, ReactJS, метки:
image

Ребята из 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. Как всегда рад услышать ваши замечания и дополнения.

Автор: BoryaMogila

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js