Open
16

自分のポートフォリオを作成する

import React from "react"

export default function Home() {
  return <div>Hello world!</div>
}

JSXで上のようにreactをインポートして書くと
以下のように、createElementに変換される。
Gatsby(Babelらへん)がSSGするときに変換してくれる。
(たしかフロントのReactに情報を投げるときは文字列に変換されて、クライアント側で再構築するはず。。。)

import React from "react"

export default function Home() {
  return React.createElement("div", null, "Hello world!")
}

https://www.gatsbyjs.com/docs/tutorial/part-two/#-style-a-component-using-css-modules

~.module.cssはmodule-cssという有名パッケージで、クラス名が一意になる。

jsファイル内でこの~.module.cssをインポートすると以下が発生する。

  • jsファイル内ではclassName = {styles.username}のように、インポートしたCSSファイルstyles(仮の名前 別の名前になることも当然)のクラス名定義usernameを利用する。 もとの~.module.cssファイルではusername {padding:0;}のように設定されている。
  • GatsbyではこのようにCSSファイルをJSで読み込むだけで自動でCSSが設定される。そして、このCSSファイルはインポート時に名前が一意になるので衝突しない。

https://www.gatsbyjs.com/docs/tutorial/part-three/#-add-a-site-title

Layoutコンポーネントを作っている。
(ただ、このページでやっているのはReactコンポーネントに<Layout>渡したい要素</Layout>)として、Layoutコンポーネントにchildrenとして渡している。

export default function Layout({ children }) {
  return (
    <div style={{ margin: `3rem auto`, maxWidth: 650, padding: `0 1 rem` }}>
      {children}
    </div>
  )
}

以下のように、Layoutコンポーネントを作成しておく。
ヘッダーやフッター、サイドバーなどすべてのページで共通化するものをここで書く。
Headerなどもコンポーネントにして外部に出すと良さそう。
結局CSSがわからんになっている。

import React from "react"
import { Link } from "gatsby"

const ListLink = props => (
  <li style={{ display: `inline-block`, marginRight: `1rem` }}>
    <Link to={props.to}>{props.children}</Link>
  </li>
)

export default function Layout({ children }) {
  return (
    <div style={{ margin: `3rem auto`, maxWidth: 650, padding: `0 1 rem` }}>
      <header style={{ marginBottom: `1.5rem` }}>
        <Link to="/" style={{ textShadow: `none`, backgroundImage: `none` }}>
          <h3 style={{ display: `inline` }}>MySweetSite</h3>
        </Link>
        <ul style={{ listStyle: `none`, float: `right` }}>
          <ListLink to="/">Home</ListLink>
          <ListLink to="/about/">About</ListLink>
          <ListLink to="/contact/">Contact</ListLink>
        </ul>
      </header>
      {children}
    </div>
  )
}

https://www.gatsbyjs.com/docs/tutorial/part-four/

GatsbyはGraphQLでデータをいろんなところから持ってくる事ができる。
使い方としては、ページJSファイルでまずコンポーネントにdataオブジェクトを受け取れるようにする。(Gatsbyのエコシステムから与えられる。)

そして、ページ下部でクエリを書く。
今回はsite.siteMetadata.titleを持ってくるものになっている。
おそらくこのsiteがgatsby-config.jsになっている。

import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"

export default function About({ data }) {
  return (
    <Layout>
      <h1>About {data.site.siteMetadata.title}</h1>
      <p>
        We're the only site running on your computer dedicated to showing the
        best photos and videos of pandas eating lots of food.
      </p>
    </Layout>
  )
}

export const query = graphql`
  query {
    site {
      siteMetadata {
        title
      }
    }
  }
`

Gatsbyv2からページコンポーネントでなくても(pages/*でなくても)、useStaticQueryでクエリを実行できるようになった。

これで以下のlayout.jsでもクエリを実行してデータを取得できる。
(ページコンポーネントではクエリを宣言するだけ。 Gatsby側でおそらく呼び出している。)

import React from "react"
import { css } from "@emotion/react"
import { Link, graphql, useStaticQuery } from "gatsby"

import { rhythm } from "../utils/typography"

export default function Layout({ children }) {
  const data = useStaticQuery(
    graphql`
      query {
        site {
          siteMetadata {
            title
          }
        }
      }
    `
  )
  return (
    <div
      css={css`
        margin: 0 auto;
        max-width: 700px;
        padding: ${rhythm(2)};
        padding-top: ${rhythm(1.5)};
      `}
    >
      <Link to={`/`}>
        <h3
          css={css`
            margin-bottom: ${rhythm(2)};
            display: inline-block;
            font-style: normal;
          `}
        >
          {data.site.siteMetadata.title}
        </h3>
      </Link>
      <Link
        to={`/about/`}
        css={css`
          float: right;
        `}
      >
        About
      </Link>
      {children}
    </div>
  )
}

https://www.gatsbyjs.com/docs/tutorial/part-five/

Gatsbyではプラグインを使ってローカルファイル・WordPressなど様々なシステムから情報をGraphQLで取得できる。

クエリはページコンポーネントであればクエリを、それ以外のコンポーネントではStaticQueryを使う。

得たデータをもとにmapなどを使って展開してJSXを生成すればいい。

import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"

export default function MyFiles({ data }) {
  console.log(data)
  return (
    <Layout>
      <div>
        <h1>My Site's Files</h1>
        <table>
          <thead>
            <th>relativePath</th>
            <th>prettySize</th>
            <th>extension</th>
            <th>birthTime</th>
          </thead>
          <tbody>
            {data.allFile.edges.map(({ node }, index) => (
              <tr key={index}>
                <td>{node.relativePath}</td>
                <td>{node.prettySize}</td>
                <td>{node.extension}</td>
                <td>{node.birthTime}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </Layout>
  )
}

export const query = graphql`
  query {
    allFile {
      edges {
        node {
          relativePath
          prettySize
          extension
          birthTime(fromNow: true)
        }
      }
    }
  }
`

https://www.gatsbyjs.com/docs/tutorial/part-six/

GatsbyではGraphQLでソースプラグインと併用してデータを持ってくる。
そして、トランスフォーマープラグインを使ってデータを変形してHTML/Reactなどで扱いやすい形式に変換する。
たとえば、fileSystemプラグインでローカルのマークダウンファイルを取得して、それをp, div, h1~4のようなHTMLに変換する変換プラグインを利用する。

また、GraphQLにはソートやFilter、LIMITなども利用できる。便利〜

import React from "react"
import { graphql } from "gatsby"
import { css } from "@emotion/react"
import { rhythm } from "../utils/typography"
import Layout from "./../components/layout"

export default function Home({ data }) {
  console.log(data)
  return (
    <Layout>
      <div>
        <h1
          css={css`
            display: inline-block;
            border-bottom: 1px solid;
          `}
        >
          Amazing Pandas Eating Things
        </h1>
        <h4>{data.allMarkdownRemark.totalCount} Posts</h4>
        {data.allMarkdownRemark.edges.map(({ node }) => (
          <div key={node.id}>
            <h3
              css={css`
                margin-bottom: ${rhythm};
              `}
            >
              {node.frontmatter.title}{" "}
              <span
                css={css`
                  color: #bbb;
                `}
              >
                - {node.frontmatter.date}
              </span>
            </h3>
            <p>{node.excerpt}</p>
          </div>
        ))}
      </div>
    </Layout>
  )
}

export const query = graphql`
  query {
    allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
      totalCount
      edges {
        node {
          id
          frontmatter {
            title
            date(formatString: "DD MMMM, YYYY")
          }
          excerpt
        }
      }
    }
  }
`

https://www.gatsbyjs.com/docs/tutorial/part-seven/

Gatsby+GraphQLでページを実際に作成しようというもの。

ページを生成するためにはgatsby-node.jsをトップディレクトリにおいて生成する。
onCreateNodeはGatsby生成時にフックされるメソッドであり、これを既定してあげる。
GraphQLのノードが作成されるときに実行されるときにフックされる、という意味であり、新しくノードを作るわけではない。
slugを新しくフィールドとしてNodeに追加するなど、更新するという感じに近い。

そして、更新されたら最期にcreatePagesでページが生成される。
この関数がフックされたときに

  • graphqlで非同期処理をしてGraphQLを叩いてマークダウンファイルを持ってくる
  • 持ってきたデータをもとにcreatePageする。path, componentを指定することで、そのページを生成できる。

componentはtemplates/~.jsに書くと良い。
このとき、テンプレート側でももっかいGraphQLを叩いている。
gatsby-node.jsでcontext slugとして渡しているので、blog-post.js側でslugでクエリを発行して関係するノードを取り出している。

  result.data.allMarkdownRemark.edges.forEach(({ node }) => {
    createPage({
      path: node.fields.slug,
      component: path.resolve(`./src/templates/blog-post.js`),
      context: {
        slug: node.fields.slug,
      },
    })
  })

gatsby-node.js

const path = require("path")
const { createFilePath } = require(`gatsby-source-filesystem`)

exports.onCreateNode = ({ node, getNode, actions }) => {
  const { createNodeField } = actions
  if (node.internal.type === `MarkdownRemark`) {
    const slug = createFilePath({ node, getNode, basePath: `pages` })
    createNodeField({
      node,
      name: `slug`,
      value: slug,
    })
  }
}

exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions
  const result = await graphql(`
    query {
      allMarkdownRemark {
        edges {
          node {
            fields {
              slug
            }
          }
        }
      }
    }
  `)
  result.data.allMarkdownRemark.edges.forEach(({ node }) => {
    createPage({
      path: node.fields.slug,
      component: path.resolve(`./src/templates/blog-post.js`),
      context: {
        slug: node.fields.slug,
      },
    })
  })
}

blog-post.js

import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"

export default function BlogPost({ data }) {
  const post = data.markdownRemark
  return (
    <Layout>
      <div>
        <h1>{post.frontmatter.title}</h1>
        <div dangerouslySetInnerHTML={{ __html: post.html }}></div>
      </div>
    </Layout>
  )
}

export const query = graphql`
  query($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      html
      frontmatter {
        title
      }
    }
  }
`

https://www.gatsbyjs.com/docs/tutorial/part-eight/

HealmetはReactでheadのMetadataを動的に変更できる。

seo.jsコンポーネントをcomponentsに作る。
このSEOコンポーネントはHealmetコンポーネントを内部で利用している。

seo.js

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 }) {
  const { site } = useStaticQuery(
    graphql`
      query {
        site {
          siteMetadata {
            title
            description
            author
          }
        }
      }
    `
  )

  const metaDescription = description || site.siteMetadata.description

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

SEO.defaultProps = {
  lang: `en`,
  meta: [],
  description: ``,
}

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

export default SEO

ログインするとコメントできます