Blog

Gatsby cz. 5 - pluginy

Data publikacji: 2021-07-28

Stały adres serii wpisów o Gatsbym - /blog/gatsby

Wstęp

W tym wpisie omówię kilka podstawowych pluginów, w większości przypadków są niezbędne, lub przynajmniej bardzo potrzebne. Szczegóły dotyczące instalacji i umieszczenia deklaracji w pliku /gatsby-config.js są w podanych linkach do pluginów.

W kilku przypadkach pokażę jak powinna wyglądać konfiguracja.

Pakiety instaluje się z promptem umieszczonym w katalogu głównym strony.

  • gatsby-source-filesystem / dostęp do plików
  • gatsby-plugin-image / obrazki
  • gatsby-plugin-react-helmet / SEO, tagi meta w nagłówku
  • gatsby-plugin-google-gtag / Analityka Google'a
  • gatsby-plugin-styled-components / CSS styled-components
  • gatsby-remark-vscode / prezentacja kodu
  • gatsby-plugin-sitemap / sitemap.xml
  • gatsby-plugin-breadcrumb / breadcrumbs

Dostęp do plików: gatsby-source-filesystem

To jest plugin do tego stopnia niezbędny, że w sumie dziwi mnie, że nie jest zintegrowany z frameworkiem. W przypadku niektórych pluginów w ogóle się nie wspomina, że "a i weź jeszcze zainstaluj source-filesystem", raczej zakłada się, że już jest.

Wskazuje on Gatsby'emu ścieżki dostępu do plików poza katalogiem pages i jak widać poniżej dwie podstawowe opcje to nazwa i ścieżka dostępu. Dla każdej ścieżki wypisuje się osobny resolve. Wskazane katalogi muszą istnieć.

Najczęściej dotyczy to katalogu z obrazkami, jeżeli mamy jakieś inne pliki mediów, np. filmy, pliki dźwiękowe, .svg to zaleca się stworzenie katalogu /assets, który zawiera je wszystkie.

/gastby-config.js

        {
            resolve: `gatsby-source-filesystem`,
            options: {
                name: `article`,
                path: `${__dirname}/src/articles`,
            },
        },
        {
            resolve: `gatsby-source-filesystem`,
            options: {
                name: `image`,
                path: `${__dirname}/src/images`,
            },
        },

Obrazki: gatsby-plugin-image

Plugin, który zastąpił przedtem używany gatsby-image. Dokładny opis używania we wpisie Gatsby cz. 4 - GraphQL & obrazki.

W zasadzie jest możliwe umieszczanie obrazków bez żadnego z tych pluginów, ale - najkrócej mówiąc - nie warto próbować.

npm i gatsby-plugin-image gatsby-plugin-sharp gatsby-source-filesystem gatsby-transformer-sharp

SEO: gatsby-plugin-react-helmet

Użytkownicy Reacta znają dobrze pakiet react-helmet, który umożliwia wpisanie tagów meta i innych parametrów do nagłówka strony. To jest wersja dla Gatsby'ego.

npm i gatsby-plugin-react-helmet react-helmet

Potem tworzymy komponent seo.js.

I teraz - uwaga - długi listing całego pliku komponentu SEO, którego używam. Różni się trochę od przedstawionego w instrukcji, wg mnie jest czytelniejszy i łatwiejszy w edycji. Jednym z powodów dla którego go wklejam, są wysokie wymagania Twittera. W czasie kiedy to piszę (lipiec 2021), obrazek musi mieć min 1200 px szerokości (po co?) i musi być wskazany ścieżką bezwzględną. Ponadto zakładam, że obrazek powinien być, ale może go zabraknąć, dlatego w propTypes nie jest wymagany.

/src/components/seo.ja

import React from "react"
import PropTypes from "prop-types"
import { Helmet } from "react-helmet"
import { useStaticQuery, graphql } from "gatsby"

function SEO({ description, lang, meta, title, image, location }) {
    const pageTitle = title;
    const { site } = useStaticQuery(
        graphql`
      query {
        site {
          siteMetadata {
            title
            description
            author
            image
            siteUrl
          }
        }
      }
    `
    )

    const twitterImage = site.siteMetadata.siteUrl + image
    const metaDescription = description || site.siteMetadata.description
    const metaTitle = title || site.siteMetadata.title

    return (
        <Helmet
            htmlAttributes={{ lang }}
            title={`${title} || ${pageTitle}`}
            titleTemplate={`${site.siteMetadata.title} ${metaTitle}`}
            image={`${image}`}
            meta={[
                {
                    name: `description`,
                    content: metaDescription,
                },
                {
                    property: `og:title`,
                    content: title,
                },
                {
                    property: `og:description`,
                    content: metaDescription,
                },
                {
                    property: `og:type`,
                    content: `website`,
                },
                {
                    property: "og:image",
                    content: image,
                },
                {
                    name: `twitter:card`,
                    content: `summary`,
                },
                {
                    name: `twitter:creator`,
                    content: site.siteMetadata.author,
                },
                {
                    name: `twitter:title`,
                    content: title,
                },
                {
                    name: `twitter:description`,
                    content: metaDescription,
                },
                {
                    property: "twitter:image:src",
                    content: twitterImage,
                },
            ].concat(meta)}
        />
    )
}

SEO.defaultProps = {
    lang: `pl`,
    meta: [],
    description: ``,
    title: "",
    image: null,
}

SEO.propTypes = {
    description: PropTypes.string,
    lang: PropTypes.string,
    meta: PropTypes.arrayOf(PropTypes.object),
    title: PropTypes.string.isRequired,
    image: PropTypes.string,
}

export default SEO

Potem taki komponent importujemy do pliku strony, layoutu, czy szablonu i przekazujemy do niego propsy. Może wyglądać to tak:

import React from "react";
import { Link, graphql } from "gatsby";
import Layout from "../components/layout"
import SEO from "../components/seo";
//

const blogPage = ({ data }) => {

   const { frontmatter, id, body, slug } = data.mdx;
   const { title: pageTitle, date, edited, image } = frontmatter;
   const headerTitle = `Tytuł strony wpisany na stałe: ${frontmatter.title}`

   return (
      <Layout data={data} path={path}>
         <SEO title={headerTitle} image={image.childImageSharp.gatsbyImageData.images.fallback.src} defer={false} />
//

Tutaj mamy dane strony i adres obrazka przesłany z frontmattera. Źródłem tych danych mogą być również propsy przesłane w komponencie layoutu.

Analityka Google Analytics: gatsby-plugin-google-gtag

To jest plugin, który zastąpił dotychczasowe, poprzednie pluginy Google Analytics.

npm i gatsby-plugin-google-gtag

Tutaj mam okazję zaprezentować większy kawałek pliku gatsby-config.js. Identyfikator usługi Analytics lepiej jest ukryć w pliku .env.

  • Na początku pliku mamy wskazanie ścieżki do pliku .env.
  • Niżej w eksporcie modułów zadeklarowany jest moduł dotenv.
  • Na końcu zadeklarowany identyfikator usługi jako zmienna środowiskowa.

/gatsby-config.js

require("dotenv").config({
    path: `.env`,
})

module.exports = {
    siteMetadata: {
// sitemetadata
    },    
    plugins: [
        "dotenv",
        {
            resolve: `gatsby-plugin-google-gtag`,
                 trackingIds: [
                    process.env.GA_ID,  // Google Analytics / GA
                ],
                gtagConfig: {                    
                    anonymize_ip: true,
                    cookie_expires: 0,
                },
            },
        },
    ],
}

Pliku env co do zasady nie wysyłamy na GitHuba ani inne publiczne miejsce. Musi zostać dodany do .gitignore. Wszyscy dostawcy usług online umożliwiają wpisanie tych parametrów na stronie podczas uploadu aplikacji.

Przeglądanie lokalnego builda dodaje się do statystyk.

CSS: Styled Components: gatsby-plugin-styled-components

W przypadku małych stron wystarczą style globalne lub po prostu importowane do layoutu. Ale przy większych projektach prawie zawsze najlepszym rozwiązaniem jest Styled Components.

Więcej o Styled Components w Gatsbym: Gatsby cz. 3 - CSS # Styled Components

npm i gatsby-plugin-styled-components styled-components babel-plugin-styled-components

Prezentowanie kodu: gatsby-remark-vscode

Tutaj jest wiele możliwości. Przede wszystkim zależy to od tego, czy treść strony umieszczamy w plikach JSX, czy w Markdownie.

W przypadku JSX wygląda to tak:

  • PrismJS
  • Highlight
  • Backtiki i nawiasy klamrowe wymagają znaku ucieczki.

/src/pages/any-page-with-code.js

import * as React, { useEffect } from 'react'
import Prism from "prismjs"
import "../../../styles/prism.css"

const PageWithCode = () => {

  useEffect(() => {
    Prism.highlightAll()
  }, [])

  return (
    <LayoutJS>
      <p>treść:</p>

      <pre>
        <code className="language-javascript">
          {`
 npm i express -S
  `}
        </code>
      </pre>

W przypadku Markdowna najlepszym rozwiązaniem jest wzmiankowany tu gatsby-remark-vscode.

npm i gatsby-plugin-styled-components styled-components babel-plugin-styled-components

A co jeżeli zdecydowaliśmy się na MDX? Jak sama nazwa wskazuje, gatsby-remark-vscode jest pluginem Remarka i jego konfiguracja wg instrukcji nie zadziała. To są dwa oddzielne procesory.

Rozwiązanie jest proste, wystarczy zadeklarować ten moduł wewnątrz deklaracji MDX, tak jak to jest zaprezentowane poniżej:

/gastby-config.js

       {
            resolve: `gatsby-plugin-mdx`,
            options: {
                extensions: [".mdx", ".md"],
                gatsbyRemarkPlugins: [
                       {
                        resolve: `gatsby-remark-vscode`,
                        options: {
                            theme: {
                                default: 'Default Dark+',
                            },
                            inlineCode: {
                                marker: '',
                                theme: {
                                    default: 'Default Light+',
                                }
                            }
                        },
                    },
                ],
            },
        },

Instalacja

npm i gatsby-plugin-breadcrumb

Konfiguracja dla układu treści opisanego w pierwszym wpisie.

/gastby-config.js

        {
            resolve: `gatsby-plugin-breadcrumb`,
            options: {
                useAutoGen: true,
                autoGenHomeLabel: `Strona główna`,
                exclude: [
                    `/404/`,
                    `/blog/`,
                ],
                // crumbLabelUpdates: optional, update specific crumbLabels in the path
                crumbLabelUpdates: [
                    {
                        pathname: '/turystyka',
                        crumbLabel: 'Turystyka'
                    },
                    {
                        pathname: '/turystyka/brandenburgia',
                        crumbLabel: 'Brandenburgia'
                    },
                    {
                        pathname: '/turystyka/saksonia',
                        crumbLabel: 'Saksonia'
                    },
                    {
                        pathname: '/it',
                        crumbLabel: 'IT'
                    },
                                        {
                        pathname: '/it/js',
                        crumbLabel: 'JavaScript'
                    },
                                        {
                        pathname: '/it/css',
                        crumbLabel: 'CSS'
                    },
                ],
                trailingSlashes: false,
            },
        },

W przypadku JSX:

/src/pages/any-page-with-breadcrumb.js

import * as React from 'react'
import { Breadcrumb } from 'gatsby-plugin-breadcrumb'

const PageWithBreadcrumb = ({ pageContext }) => {
	const {
		breadcrumb: { crumbs },
	} = pageContext

	return (
		<Layout>
			<Breadcrumb
				location={location}
				crumbs={crumbs}
				crumbSeparator=" / "
				crumbLabel="Tytuł do wrzucenia w breadcrumb"
			/>

Jeżeli używamy MDX wygląda to tak samo, tyle że w pliku szablonu strony /src/templates/articleTemplate.js z tą różnicą, że crumbLabel najlepiej jest zadeklarować z frontmattera:

        crumbLabel={frontmatter.title}