自分のポートフォリオを作成する
Gatsbyを触っていく
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!")
}
gatsby-browser.jsは Gatsby全体のサイトの設定を行うページらしい。
ここでCSSファイルをインポートすると全体にデザインが効く
ただし、以下のほうがモダンで正しいらしい
~.module.css
はmodule-cssという有名パッケージで、クラス名が一意になる。
jsファイル内でこの~.module.cssをインポートすると以下が発生する。
- jsファイル内では
className = {styles.username}
のように、インポートしたCSSファイルstyles
(仮の名前 別の名前になることも当然)のクラス名定義usernameを利用する。 もとの~.module.cssファイルではusername {padding:0;}のように設定されている。 - GatsbyではこのようにCSSファイルをJSで読み込むだけで自動でCSSが設定される。そして、このCSSファイルはインポート時に名前が一意になるので衝突しない。
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>
)
}
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>
)
}
はえ〜
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)
}
}
}
}
`
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
}
}
}
}
`
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
}
}
}
`
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
チュートリアルが終わったので、次の段階へ
ドキュメント
How-To-Guide (おそらくトピックごとの内容)
コンセプトガイド
デフォルトスターター
ブログスターター
できた!
Gatsby + Tailwind でつくった!