Jak vytvořit vlastní komponentu React pro vykreslování značek metadat SEO a jaké značky zahrnout, abyste zlepšili hodnocení a výkon na webu.
Začínáme
Pro tento tutoriál použijeme CheatCode Next.js Boilerplate jako výchozí bod pro naši práci. Pro začátek naklonujme kopii z Github:
Terminál
git clone https://github.com/cheatcode/nextjs-boilerplate
Dále cd
do projektu a nainstalujte jeho závislosti:
Terminál
cd nextjs-boilerplate && npm install
Nakonec pokračujte a spusťte vývojový server:
Terminál
npm run dev
Díky tomu jsme připraveni začít.
Vytvoření SEO komponenty
Pro tento tutoriál vytvoříme komponentu React, která nám pomůže vykreslit naše SEO data v Next.js. To nám usnadní přidávání SEO metadat na jakoukoli stránku, stačí pouze změnit rekvizity, které předáme komponentě.
/components/SEO/index.js
import React from "react";
import PropTypes from "prop-types";
import Head from "next/head";
import settings from "../../settings";
const SEO = (props) => {
const { title, description, image } = props;
return (
<Head>
<title>{title} | App</title>
<meta name="description" content={description} />
<meta itemprop="name" content={title} />
<meta itemprop="description" content={description} />
<meta itemprop="image" content={image} />
</Head>
);
};
SEO.defaultProps = {
title: settings && settings.meta && settings.meta.title,
description: settings && settings.meta && settings.meta.description,
image:
settings &&
settings.meta &&
settings.meta.social &&
settings.meta.social.graphic,
};
SEO.propTypes = {
title: PropTypes.string,
description: PropTypes.string,
image: PropTypes.string,
};
export default SEO;
Zaměříme-li se na horní část souboru, pro naši komponentu React použijeme vzor komponenty funkce, protože pro tuto komponentu nepotřebujeme přístup k žádným metodám životního cyklu nebo stavu.
V horní části souboru importujeme další komponentu React z next/head
balíček, <Head />
. Toto je komponenta, která je integrována do Next.js a pomáhá nám definovat data, která se budou zobrazovat (automaticky pomocí Next.js) v <head></head>
tagy HTML pro stránku, kde je tato komponenta vykreslena.
Pro návratovou hodnotu naše komponenta — <SEO />
—vykreslíme tento <Head />
komponentu jako značku pro otevření a zavření. To pro React znamená, že obsah mezi tagem open a close jsou potomky této komponenty. Ačkoli to nevidíme, React má standardní podporu children
kterému je přiřazeno označení předané mezi otevřenou a zavřenou značkou, jak vidíme výše. Interně <Head />
komponenta přečte tento children
prop a používá jej k naplnění <head></head>
tag ve vykresleném HTML pro stránku (co se odešle zpět do Googlu a dalších vyhledávačů).
Mezi těmito značkami předáváme standardní HTML <title />
tag spolu se sérií <meta />
značky. I když jsme v komponentě React, toto označení představuje prosté HTML. Pokud bychom měli zkopírovat tyto značky a vložit je do <head></head>
z prostého .html
soubor, prohlížeč by je vykreslil bez problémů.
Zde, protože jsme uvnitř Reactu – nebo přesněji JSX, značkovacího jazyka, který React používá – můžeme předat dynamické hodnoty (zde známé jako výraz React) do atributů těchto značek pomocí složených závorek. Ve výše uvedeném kódu, přímo v těle funkce pro naši komponentu, používáme destrukci objektů JavaScriptu k „odstranění“ title
, description
a image
očekáváme, že budou předány našemu <SEO />
komponenta.
Za předpokladu, že jsou definovány, nastavíme je v našem označení metadat pomocí výrazů React, čímž se vykreslí title
v <title></title>
tag a ostatní jako content
atribut jejich příslušných <meta />
značky. Zde je důležité poznamenat:protože se v zájmu SEO snažíme pokrýt všechny naše základy, budeme svědky toho, že se data předávají různým značkám vícekrát. Toto je záměrné. Je to proto, že různé vyhledávače analyzují naše data různými způsoby. Tímto způsobem zajišťujeme maximální kompatibilitu našeho obsahu.
V dolní části našeho souboru si všimneme, že využíváme .defaultProps
Reactu a .propTypes
atributy pro naši komponentu. Ten druhý, .propTypes
, má za cíl pomoci nám ověřit obsah rekvizit, které nám byly předány. To je pro nás jako vývojáře a neovlivňuje to naše uživatele. Zde pomocí PropTypes
objekt, který jsme importovali nahoře, jsme nastavili očekávání našich tří rekvizit title
, description
a image
všechny obsahují řetězcovou hodnotu (v našem kódu označenou jako PropTypes.string
).
Těsně nad tím také definujeme nějaký defaultProps
pro naše <SEO />
komponent. Toto je důležité. Všimněte si, že zde přistupujeme k hodnotě settings
předpokládáme, že jde o objekt importovaný odjinud v našem projektu. V základní verzi, kterou jsme naklonovali na začátku tutoriálu, existuje konvence pro načítání souboru libovolných nastavení na základě aktuálního prostředí nebo hodnoty process.env.NODE_ENV
. Ve výchozím nastavení je tato hodnota development
a tak očekáváme, že základní deska načetla obsah /settings/settings-development.js
soubor pro nás.
/settings/settings-development.js
const settings = {
graphql: {
uri: "http://localhost:5001/api/graphql",
},
meta: {
rootUrl: "http://localhost:5000",
title: "App",
description: "The app description goes here.",
social: {
graphic:
"https://cheatcode-assets.s3.amazonaws.com/default-social-graphic.png",
twitter: "@cheatcodetuts",
},
},
routes: {
authenticated: {
pathAfterFailure: "/login",
},
public: {
pathAfterFailure: "/documents",
},
},
};
export default settings;
Všimněte si, že v těchto nastaveních uvidíme meta
objekt je nastaven na sérii párů klíč/hodnota. Tato data jsou nastavena jako výchozí metadata SEO pro celý náš web (nebo, řečeno jiným způsobem, záložní data, na která se budeme spoléhat, pokud nepředáme žádné hodnoty pro rekvizity našeho <SEO />
komponenta).
/components/SEO/index.js
import React from "react";
import PropTypes from "prop-types";
import Head from "next/head";
import settings from "../../settings";
const SEO = (props) => {
const { title, description, image } = props;
return (
<Head>
<title>{title} | App</title>
<meta name="description" content={description} />
<meta itemprop="name" content={title} />
<meta itemprop="description" content={description} />
<meta itemprop="image" content={image} />
</Head>
);
};
SEO.defaultProps = {
title: settings && settings.meta && settings.meta.title,
description: settings && settings.meta && settings.meta.description,
image:
settings &&
settings.meta &&
settings.meta.social &&
settings.meta.social.graphic,
};
SEO.propTypes = {
title: PropTypes.string,
description: PropTypes.string,
image: PropTypes.string,
};
export default SEO;
Zpět v naší komponentě vidíme, že vtahujeme tento soubor nastavení a náš .defaultProps
objekt, který předá obsah meta
objekt v tomto souboru. Opět to zajistí, že pokud to uděláme ne předejte tyto rekvizity, budeme mít nějaké data předaná jako protiklad k prázdnému řetězci nebo "nedefinované" hodnotě.
Přidání značek metadat pro sociální média
Zatímco kód, na který jsme se podívali výše, nám jistě pomůže začít s našimi potřebami SEO, v moderních webových podmínkách by to bylo jako přinést nůž do přestřelky. Protože se web rozšířil do různých sociálních sítí a stále složitějších algoritmů vyhledávačů, pomáhá našemu případu kvůli hodnocení přidat konkrétnější údaje.
Konkrétně chceme přidat podporu pro sociální data ze dvou velkých webů:Twitter a Facebook. Naštěstí, i když budeme muset podporovat více značek, struktura těchto značek je dostatečně podobná, že můžeme automatizovat většinu jejich výstupu.
/components/SEO/index.js
import React from "react";
import PropTypes from "prop-types";
import Head from "next/head";
import settings from "../../settings";
const socialTags = ({
openGraphType,
url,
title,
description,
image,
createdAt,
updatedAt,
}) => {
const metaTags = [
{ name: "twitter:card", content: "summary_large_image" },
{
name: "twitter:site",
content:
settings &&
settings.meta &&
settings.meta.social &&
settings.meta.social.twitter,
},
{ name: "twitter:title", content: title },
{ name: "twitter:description", content: description },
{
name: "twitter:creator",
content:
settings &&
settings.meta &&
settings.meta.social &&
settings.meta.social.twitter,
},
{ name: "twitter:image:src", content: image },
{ name: "twitter:card", content: "summary_large_image" },
{ name: "og:title", content: title },
{ name: "og:type", content: openGraphType },
{ name: "og:url", content: url },
{ name: "og:image", content: image },
{ name: "og:description", content: description },
{
name: "og:site_name",
content: settings && settings.meta && settings.meta.title,
},
{
name: "og:published_time",
content: createdAt || new Date().toISOString(),
},
{
name: "og:modified_time",
content: updatedAt || new Date().toISOString(),
},
];
return metaTags;
};
const SEO = (props) => {
const { url, title, description, image } = props;
return (
<Head>
<title>{title} | App</title>
<meta name="description" content={description} />
<meta itemprop="name" content={title} />
<meta itemprop="description" content={description} />
<meta itemprop="image" content={image} />
{socialTags(props).map(({ name, content }) => {
return <meta key={name} name={name} content={content} />;
})}
</Head>
);
};
SEO.defaultProps = {
url: "/",
openGraphType: "website",
...
};
SEO.propTypes = {
url: PropTypes.string,
openGraphType: PropTypes.string,
...
};
export default SEO;
Než se pustíme do sociálních značek, opravdu rychle, chceme upozornit na url
a openGraphType
pole, která jsme přidali do našeho propTypes
a defaultProps
. Ty představují url
stránky, na které se právě nacházíme (např. pokud jsme na blogu jako /posts/the-slug-of-the-post
) a openGraphType
který se bude mapovat na hodnotu typu z definice typů objektů protokolu Open Graph.
Část, kterou jsme skutečně péče o zde je v naší návratové hodnotě:nové .map()
děláme.
Zde jsme zavedli funkci nahoře, která vrací pole objektů, přičemž každý objekt obsahuje hodnotu pro name
a content
atribut na <meta />
štítek. Všimněte si, že názvy se mění podle konkrétní sociální sítě, ale struktura ne . Toto je záměrné.
Zatímco Twitter a Facebook (og
zde znamená „Otevřený graf“, což je standard vytvořený Facebookem) mají své vlastní jedinečné názvy metadat, oba používají stejný mechanismus ke sdílení těchto dat. V našem kódu toho můžeme využít a zacyklit se přes pole objektů, z nichž každý vyplivne <meta />
tag předáním name
a content
pro aktuální položku, kterou procházíme jako atributy na značce.
K provedení této smyčky zavoláme socialTags()
nejprve předejte props
pro naši komponentu a poté dynamicky naplnit pole objektů, které funkce vrací, těmito hodnotami prop. Na oplátku dostaneme zpět pole objektů, které očekáváme v našem return
hodnotu.
Tam zřetězujeme volání na .map()
na naše volání na číslo socialTags(props)
a pro každou položku ve vráceném poli vykreslete <meta />
tag s odpovídajícími atributy pro daný objekt.
Je důležité si uvědomit:to, co vidíte, je jen nějaké dostupných metaznaček pro Twitter a Facebook. V závislosti na vašem vlastním webu možná budete chtít zahrnout méně nebo více značek.
V případě Twitteru se můžete odkázat na jejich dokumentaci ke značení karet a v případě Facebooku na dokumentaci protokolu Open Graph.
Když jsou tyto prvky na místě, nyní, když je náš obsah sdílen na Twitteru nebo Facebooku, získáme správně zobrazený prvek „karty“, který vypadá hezky v časové ose lidí.
Přidávání metadat Google JSON-LD
Než vložíme naše <SEO />
chceme přidat ještě jeden typ metadat:JSON-LD společnosti Google ("LD" znamená "Linking Data"). Toto jsou data, která Google používá pro funkce, jako jsou jejich informační karty ve výsledcích vyhledávání.
/components/SEO/index.js
import React from "react";
import PropTypes from "prop-types";
import Head from "next/head";
import settings from "../../settings";
const socialTags = ({
openGraphType,
url,
title,
description,
image,
createdAt,
updatedAt,
}) => { ... };
const SEO = (props) => {
const { url, title, description, image, schemaType } = props;
return (
<Head>
...
{socialTags(props).map(({ name, content }) => {
return <meta key={name} name={name} content={content} />;
})}
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify({
"@context": "http://schema.org",
"@type": schemaType,
name: title,
about: description,
url: url,
}),
}}
/>
</Head>
);
};
SEO.defaultProps = {
url: "/",
openGraphType: "website",
schemaType: "Article",
...
};
SEO.propTypes = {
url: PropTypes.string,
openGraphType: PropTypes.string,
schemaType: PropTypes.string,
...
};
export default SEO;
Přímo pod našimi sociálními značkami .map()
, nyní jsme přidali <script />
tag s type
atribut nastaven na application/ld+json
(typ MIME, který Google hledá při kontrole dat JSON-LD). Protože data pro JSON-LD se obvykle předávají jako objekt mezi script tags, musíme to "React-ify" to "React-ify", abychom nedostali žádné runtime chyby.
K tomu využíváme React's dangerouslySetInnerHTML
prop, předá mu objekt s __html
atribut nastaven na stringovanou verzi našeho objektu JSON-LD. Když se to vykreslí, React zde dynamicky nastaví objekt jako vnitřní HTML nebo obsah našeho <script />
tag pro nás (bez ohledu na Google a funguje stejně).
Na objektu používáme strukturu JSON-LD a k popisu našich dat používáme definice typu schema.org.
A je to! Abychom to zakončili, podívejme se, jak naši komponentu používat.
Pomocí naší SEO komponenty
Abychom mohli naši komponentu používat, rychle zapojíme ukázkovou stránku do našeho standardu. Za tímto účelem vytvoříme falešný „příspěvek na blogu“ v souboru s názvem /pages/post/index.js
:
/pages/post/index.js
import React from "react";
import PropTypes from "prop-types";
import SEO from "../../components/SEO";
import StyledPost from "./index.css";
const Post = (props) => (
<StyledPost>
<SEO
url={`${props.url}/post`}
openGraphType="website"
schemaType="article"
title="The Fate of Empires"
description="The only thing we learn from history, it has been said, 'is that men never learn from history'..."
image={`${props.url}/colosseum.jpeg`}
/>
<header>
<h1>The Fate of Empires</h1>
<h5>Sir John Glubb</h5>
</header>
<div>
<img src="/colosseum.jpeg" alt="Colosseum" />
<p>
As we pass through life, we learn by experience. We look back on our
behaviour when we were young and think how foolish we were. In the same
way our family, our community and our town endeavour to avoid the
mistakes made by our predecessors.
</p>
<p>
The experiences of the human race have been recorded, in more or less
detail, for some four thousand years. If we attempt to study such a
period of time in as many countries as possible, we seem to discover the
same patterns constantly repeated under widely differing conditions of
climate, culture and religion. Surely, we ask ourselves, if we studied
calmly and impartially the history of human institutions and development
over these four thousand years, should we not reach conclusions which
would assist to solve our problems today? For everything that is
occurring around us has happened again and again before.
</p>
<p>
No such conception ever appears to have entered into the minds of our
historians. In general, historical teaching in schools is limited to
this small island. We endlessly mull over the Tudors and the Stewarts,
the Battle of Crecy, and Guy Fawkes. Perhaps this narrowness is due to
our examination system, which necessitates the careful definition of a
syllabus which all children must observe.
</p>
<p>
The only thing we learn from history,’ it has been said, ‘is that men
never learn from history’, a sweeping generalisation perhaps, but one
which the chaos in the world today goes far to confirm. What then can be
the reason why, in a society which claims to probe every problem, the
bases of history are still so completely unknown?{" "}
</p>
</div>
</StyledPost>
);
Post.propTypes = {
url: PropTypes.string.isRequired,
};
export const getServerSideProps = (context) => {
return {
props: {
url: context?.req?.headers?.host,
},
};
};
export default Post;
Část, na které nám záleží nejvíce, je vykreslení našeho <SEO />
komponent. Všimněte si, že jsme to importovali do horní části našeho souboru a vykreslujeme to přímo uvnitř <StyledPost />
zde komponenta (toto je speciální typ komponenty React známý jako stylovaná komponenta). Takže to máte, opravdu rychle, zde je zdroj pro tuto komponentu (pozor na cestu):
/pages/post/index.css.js
import styled from "styled-components";
export default styled.div`
max-width: 800px;
margin: 0 auto;
header {
margin: 25px 0;
padding: 0;
}
header h1 {
font-size: 28px;
font-weight: bold;
}
header h5 {
color: #888888;
}
div img {
max-width: 100%;
display: block;
margin: 0px 0px 25px;
}
div p {
font-size: 18px;
line-height: 28px;
}
@media screen and (min-width: 768px) {
header {
margin: 50px 0 50px;
padding: 0;
}
div img {
max-width: 100%;
display: block;
margin: 0px 0px 50px;
}
}
`;
Zde používáme styled-components
knihovna obsažená ve standardu Next.js, který používáme, aby nám pomohla dynamicky vytvořit komponentu React, která vrací HTML <div />
prvek s CSS předávaným mezi zadními značkami zde jako styly pro že <div />
. Co a proč nejsou pro tento tutoriál příliš důležité, takže po přidání tohoto souboru se vraťme na stránku s příspěvky.
/pages/post/index.js
import React from "react";
import PropTypes from "prop-types";
import SEO from "../../components/SEO";
import StyledPost from "./index.css";
const Post = (props) => (
<StyledPost>
<SEO
url={`${props.url}/post`}
openGraphType="website"
schemaType="article"
title="The Fate of Empires"
description="The only thing we learn from history, it has been said, 'is that men never learn from history'..."
image={`${props.url}/colosseum.jpeg`}
/>
<header>
<h1>The Fate of Empires</h1>
<h5>Sir John Glubb</h5>
</header>
<div>
<img src="/colosseum.jpeg" alt="Colosseum" />
<p>
As we pass through life, we learn by experience. We look back on our
behaviour when we were young and think how foolish we were. In the same
way our family, our community and our town endeavour to avoid the
mistakes made by our predecessors.
</p>
<p>
The experiences of the human race have been recorded, in more or less
detail, for some four thousand years. If we attempt to study such a
period of time in as many countries as possible, we seem to discover the
same patterns constantly repeated under widely differing conditions of
climate, culture and religion. Surely, we ask ourselves, if we studied
calmly and impartially the history of human institutions and development
over these four thousand years, should we not reach conclusions which
would assist to solve our problems today? For everything that is
occurring around us has happened again and again before.
</p>
<p>
No such conception ever appears to have entered into the minds of our
historians. In general, historical teaching in schools is limited to
this small island. We endlessly mull over the Tudors and the Stewarts,
the Battle of Crecy, and Guy Fawkes. Perhaps this narrowness is due to
our examination system, which necessitates the careful definition of a
syllabus which all children must observe.
</p>
<p>
The only thing we learn from history,’ it has been said, ‘is that men
never learn from history’, a sweeping generalisation perhaps, but one
which the chaos in the world today goes far to confirm. What then can be
the reason why, in a society which claims to probe every problem, the
bases of history are still so completely unknown?{" "}
</p>
</div>
</StyledPost>
);
Post.propTypes = {
url: PropTypes.string.isRequired,
};
export const getServerSideProps = (context) => {
return {
props: {
url: context?.req?.headers?.host,
},
};
};
export default Post;
Podívejte se na naše vykreslení <SEO />
Jak jsme naznačili během vývoje komponenty, vše, co děláme, je předávání rekvizit s daty, která chceme mapovat, do našich různých metaznaček uvnitř komponenty. Zatímco zde napevno kódujeme naše ukázkové rekvizity, technicky byste mohli (a pravděpodobně budete) použít výraz React k předání nějaké proměnné hodnoty v závislosti na tom, kde komponentu vykreslíte.
Než to nazveme hotovým, opravdu rychle, chceme upozornit na použití getServerSideProps
v dolní části našeho souboru. Toto je funkce, kterou Next.js používá k tomu, jak název napovídá, k získání jakýchkoli rekvizit pro naši komponentu v kontextu serveru před na straně serveru vykresluje naši komponentu. Toto je důležité. Vykreslování na straně serveru je termín používaný k popisu počáteční odpovědi odeslané zpět na požadavek HTTP. Tato odpověď „vykreslí“ nějaké HTML, které žadatel obdrží.
Takto fungují vyhledávače. Stránky jako Google mají „prohledávač“, který navštěvuje všechny veřejné adresy URL na internetu. Hledá tuto počáteční odpověď, aby získal HTML, který používá ke generování výsledků vyhledávání. To je přesně to, kdy očekáváme náš <SEO />
komponenta, která má být vykreslena a "vyzvednuta" vyhledávači.
Zde uvnitř getServerSideProps
chceme získat základní adresu URL (aktuální doménu) aplikace a předat ji naší komponentě jako podporu url
. Chceme to udělat tak, že když vykreslíme naše <SEO />
komponenta jako součást počáteční HTML odpovědi, URL, kterou předáme pro url
podpěra na naší komponentě je správná. Pokud neudělali Pokud to uděláte, první odpověď, kterou pošleme zpět do vyhledávače, bude mít „nedefinovanou“ adresu URL.
S tím jsme připraveni na test. Otevřeme http://localhost:5000/post
stránku v našem webovém prohlížeči a zobrazte zdroj naší stránky a zkontrolujte, zda se naše metadata vykreslují podle očekávání:
Skvělý. Protože zde vidíme vykreslená naše metadata, můžeme se spolehnout, že to je to, co Google (nebo jakýkoli jiný vyhledávač uvidí), když jejich prohledávač požádá o náš web.
Zabalení
V tomto tutoriálu jsme se naučili, jak zapojit vlastní <SEO />
komponenta React, která nám pomáhá dynamicky vykreslovat značky metadat na základě rekvizit, které jsme této komponentě předali. Naučili jsme se vykreslovat základní HTML <meta />
tagy, stejně jako tagy nezbytné pro stránky sociálních médií, jako je Twitter a Facebook. Nakonec jsme se naučili, jak přidat Google JSON-LD <script />
do naší komponenty, abychom přidali více kontextu a zlepšili naše šance na umístění ve výsledcích vyhledávání.