Gatsbyを触ってみたのですがチュートリアルだけだと??となる部分がいくつもあり結局ドキュメントをたくさん読むハメになりました。
ドキュメントのリンクを整理してたらいい感じのチュートリアルができたので投稿しました(所々中途半端ですが・・・)
(追記)実際にブログを作るときはこちらも参照いただけると良いかもです。
Install
npm install -g gatsby-cli
gatsby new [rootPath] [starter] Create new Gatsby project.
starterを指定する。デフォルトではdefault starterが選択されます。
チュートリアルなのでhello-world一択。
gatsby new my-blog https://github.com/gatsbyjs/gatsby-starter-hello-world
gatsby develop
一応npm scriptにも書かれています。
npm run develop
"scripts": {
"develop": "gatsby develop"
}
-p
オプションで別のポートを指定することができるので複数のプロジェクトを立ち上げたいときに。
gatsby develop -p 3000
CLIのドキュメントまで丁寧にあります。
デフォルトだとhttp://localhost:8000/ でページにhttp://localhost:8000/___graphql でGrqphiQLにアクセスできます。
入門
Page
ディレクトリの構成にルールがあってpages/
が自動でページなります。
例えばpages/about.js
を作成すると/about
で表示されます(ページが生成されてルートが設定される)
import React from 'react'
export default <div> index page </div>
import React from 'react'
export default <div> hello page </div>
その他はcomponents/
に共通コンポネントを作成するなど普通のReactのアプリケーションのようにを開発できます。
Any React component defined in src/pages/*.js will automatically become a page.
またテンプレートファイルを作成して動的にページを生成することも可能です。creating-pages-automatically
GraphQL
gatsbyの便利な点はプロジェクト内のデータをGraphqlで取得できるところです。
例えばProjectの設定情報は以下のqueryで取得できます。
configファイルを以下のように変更します。
module.exports = {
siteMetadata: {
title: `Title from siteMetadata`,
}
}
query {
site {
siteMetadata {
title
}
}
}
このようにプロジェクトの情報をqueryで取得して、それを表示するReactのコンポネントを作成し、それを元にgatsbyがページを生成するという流れになります。
このGraphQLですがデータを追加する作業とデータを取得する作業の二つに分けて理解するのがいいと思います。
まずデータを追加する作業から見ていきます。
後述するプラグインを入れる事でプロジェクト内の情報を自由に取得できるようになります。
プラグインからデータを追加する
データを追加する作業はドキュメントにplugin driven
と書かれていて、実際プラグインを入れるだけです。
gatsby-source-filesystem
例えばgatsby-source-filesystem
を使うことでソースからデータを取得できるようになります。
npm install --save gatsby-source-filesystem
module.exports = {
siteMetadata: {
title: `Pandas Eating Lots`,
},
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
name: `src`,
path: `${__dirname}/src/`,
},
}
]
}
graphiqlを開くとallFile
とfile
が新たに追加され、以下のqueryでファイル情報を取得できるようになります。
query {
allFile {
edges {
node {
relativePath
prettySize
extension
birthTime(fromNow: true)
}
}
}
}
チュートリアルではプロジェクトのJSファイルを取得して表示しています。
gatsby-transformer-remark
これはデータを追加するだけのプラグインではないので、この流れで書くのも微妙なのですが
このプラグインがやってくれる一番わかりやすいのはmarkdownを変換してくれるところです。mdファイルの情報をhtmlに変換して取得できたり、frontmatterなどのメタ情報を取得できます。
allMarkdownRemark
というのが追加されます。
自分でデータを追加する
createNodeAPIを使って自分でデータを流すことができます。後述しますがプラグインはこのAPIを使ってデータを流しています。
手動でデータを入れてみる
ポケモンのデータを入れてみる
exports.sourceNodes = ({ actions, createNodeId, createContentDigest }) => {
const pokemons = [
{ name: "Pikachu", type: "electric" },
{ name: "Squirtle", type: "water" },
]
pokemons.forEach(pokemon => {
const node = {
name: pokemon.name,
type: pokemon.type,
id: createNodeId(`Pokemon-${pokemon.name}`),
internal: {
type: "Pokemon",
contentDigest: createContentDigest(pokemon),
},
}
actions.createNode(node)
})
}
以下のqueryでポケモンを取得できるようになります。
query MyPokemonQuery {
allPokemon {
nodes {
name
type
id
}
}
}
外部から取得したデータを流すこともできる。
APIから取得したデータを流してみる
QiitaのAPIからデータを流してみる
この辺のAPIは詳しく確認してないので、少し修正しただけですが、
const fetch = require("node-fetch")
const fetchItems = () =>
fetch("https://qiita.com/api/v2/items").then(res => res.json())
exports.sourceNodes = async ({
actions,
createNodeId,
createContentDigest,
}) => {
const items = await fetchItems()
items.forEach(item => {
const node = {
id: item.id,
title: item.title,
user: {
name: item.user.name,
},
internal: {
type: "Item",
contentDigest: createContentDigest(item),
},
}
actions.createNode(node)
})
}
query ItemQuery {
allItem {
nodes {
title
user {
name
}
}
}
}
{
"data": {
"allItem": {
"nodes": [
{
"title": "blob & createObjectURL について ",
"user": {
"name": "Yu Watanabe"
}
},
]
}
}
}
QiitaのAPIからとってきた情報を流す事で、queryで取得できるようになりました。
先ほどのgatsby-source-filesystemもこのcreateNodeを使ってローカルのファイル一覧を取得して流し込んでいるのがわかると思います。
ファイルの情報を流すとかwordpressから記事を取得して流すといったことをやってくれるプラグインがあるので、我々は設定ファイルにパスだったりurlを入れるだけでqueryでデータを取得できるようになります。
pixabay-source-plugin-tutorial
そしてqueryで取得した結果をページに表示するだけでいいのです。
nodeを追加する
nodeにfieldを追加することができます。
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,
})
}
}
が追加されてslugが取得できるようになった。
query {
allMarkdownRemark {
edges {
node {
id
fields {
slug
}
}
}
}
}
Query
GraphQLを初めて触る人もいるかもしれませんがサイトを作るだけであればレファレンスの内容で十分だと思います。またGrqphiqlで左側をぽちぽち動かせば勝手にquery作ってくれるので特に困ることもないかと思います。
alias
allMarkdownRemark
の結果はそのままだとdata.allMarkdownRemark
ですが、先頭に任意のaliasをつけることができます。
data.latestPost
, data.relatedPost
のように帰ってきます。
query {
latestPost: allMarkdownRemark(limit: 5) { }
relatedPost: allMarkdownRemark(limit: 5) { }
}
frontmatter
マークダウンファイルの先頭に以下のように情報を定義します。
---
title: my first post
date: 2019-12-7
---
先ほどのgatsby-transformer-remark
を使うことで以下のqueryで取得することができます。
frontmatter {
title:
date:
}
params
どちらかというとmarkdown-remark
の話ですが、
- limit
allMarkdownRemark(limit: 5) { }
- format
date(formatString: "YYYY月MM月DD日")
- filter
filter: { tags: { in: ["post", "page"] }, draft: { eq: false } }
直近の投稿5件を取得するQuery
query {
allMarkdownRemark(
limit: 5,
sort: {order: DESC, fields: frontmatter___create_at}
) {
edges {
node {
id
frontmatter {
title
}
}
}
}
}
動的にページを生成する。
ページから必要なデータを取得する
実際にGraphQLからデータを取得してページに表示していきます。
import React from "react"
import { graphql } from "gatsby"
export const query = graphql`
query TitleQuery {
site {
siteMetadata {
title
}
}
}
`
export default ({ data }) => (
<div>
<h1>{data.site.siteMetadata.title}</h1>
</div>
)
- 1つのファイルにつき1つのqueryを宣言できる。この時変数は関係ない。
- queryの結果がpropsのdataオブジェクトに入ってくる。
チュートリアルには書かれていないのですが、上記のルールがあります。
試しに一つのファイルに二つのqueryを宣言してみたら怒られました。
export const query1 = graphql`
query HomePageQuery {
site {
siteMetadata {
description
}
}
}
`
export const query2 = graphql`
query HomePageQuery {
site {
siteMetadata {
description
}
}
}
`
Multiple "root" queries found in file
初めてこれを見たときは、どうしてpropsのdataにqueryの結果が渡ってくるんだ?いつqueryは実行されるという気持ちになったのですが、今使っているのは静的サイトジェネレーターでなので、お作法に従ってファイルにqueryを定義すればそれをもとにページ生成してくれるということを忘れていました。
https://www.gatsbyjs.org/docs/recipes/#querying-data-with-a-page-query
https://www.gatsbyjs.org/docs/page-query/#add-the-graphql-query
動的にページを生成する
createPage
APIを使ってテンプレートから動的にPageを生成します。
チュートリアルのpart7でこんな感じになると思います。
const path = require(`path`)
exports.createPages = async ({ actions, graphql }) => {
const result = await graphql(`
{
allMarkdownRemark {
edges {
node {
frontmatter {
path
}
}
}
}
}
`)
if (result.errors) { console.error(result.errors) }
result.data.allMarkdownRemark.edges.forEach(({ node }) => {
actions.createPage({
path: node.frontmatter.path,
component: path.resolve(`src/templates/post.js`),
})
})
}
import React from "react"
import { graphql } from "gatsby"
export const pageQuery = graphql`
query($path: String!) {
markdownRemark(frontmatter: { path: { eq: $path } }) {
html
frontmatter {
date(formatString: "MMMM DD, YYYY")
path
title
}
}
}
`
const Template = ({ data }) => {
const { markdownRemark } = dat
const { frontmatter, html } = markdownRemark
return (
<div className="blog-post">
<h1>{frontmatter.title}</h1>
<h2>{frontmatter.date}</h2>
<div
className="blog-post-content"
dangerouslySetInnerHTML={{ __html: html }}
/>
</div>
)
}
export default Template;
まず、テンプレートファイルを見てみます。テンプレートファイルはpathパラメータを使ったqueryの結果を埋め込むようになっています。
gatsby-node.js
では全てのmdファイルのquery結果を元にcreatePage
メソッドを読んでいます。
このメソッドが上のテンプレートファイルを指定していて、動的にページが生成されます。
想像はつくもののチュートリアルに書かれていない大事な点は引数のcontextがtemplateのpropsとtemplateファイルのqueryの引数として渡せる点です。
https://www.gatsbyjs.org/docs/recipes/#sourcing-markdown-data-for-blog-posts-and-pages-with-graphql
https://www.gatsbyjs.org/docs/using-gatsby-without-graphql/
All context values are made available to a template’s GraphQL queries as arguments prefaced with $
pased as props to the component this.props.pageContext as well as to the graphql query as graphql arguments.
ソースを探すの少し時間がかかりました。
概念的な
Starter
- gatsby-starter-default
- gatsby-starter-blog
- gatsby-starter-hello-world
公式のstarterは実は上の3つだけ。
スターター一覧
https://www.gatsbyjs.org/starters/?v=2
gatsby new gatsby-starter-hero-blog https://github.com/greglobinski/gatsby-starter-hero-blog
これはちょっと重かった。
Plugin
npm install --save gatsby-transformer-json
module.exports = {
plugins: [`gatsby-transformer-json`],
}
Pluginは任意のOptionを指定できる。
plugins: [
// Shortcut for adding plugins without options.
"gatsby-plugin-react-helmet",
{
// Standard plugin with options example
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/src/data/`,
name: "data",
},
}
]
StyledComponentsやaxiosなどのnpx packageを使うには
ローカルのパスを指定してプラグインを入れることもできる。
Config
gatsby-config.js
gatsyby-node.js
build時に一度だけ呼ばれ、動的にページを生成したり、GraphQLのnodeを追加したりする。
実践編
gatsby new blog https://github.com/gatsbyjs/gatsby-starter-hello-world
styled-componentsを使う
モジュールとプラグインを入れます。
npm install --save gatsby-plugin-styled-components styled-components babel-plugin-styled-components
module.exports = {
plugins: [`gatsby-plugin-styled-components`],
}
typescriptを使う
あまり参考になるかはわかりませんが、Pluginを入れる必要があります。
npm install gatsby-plugin-typescript
{
"include": ["./src/**/*"],
"compilerOptions": {
"target": "esnext",
"module": "commonjs",
"lib": ["dom", "es2017"],
"jsx": "react",
"strict": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"noEmit": true,
"skipLibCheck": true
}
}
Typescriptを使うメリットがどれくらいあるかだが、公式がPropsTypesで頑張ってるのをみると入れてもいいと思う。
Syntax Hilight
GatsbyのPluginにのっかって楽をするだけであればプラグイン入れるだけです。
npm i -S gatsby-remark-prismjs
gatsby-transformer-remark
のpluginに追加してあげます。
plugins: [
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [
`gatsby-remark-prismjs`,
]
}
}
]
先ほどのqueryがclassつきのhtmlを取得できます。
あとは自分でstyleを指定してもいいですが、標準で用意されているテーマを読み込んであげるだけです。
gatsby-browser.js
require("prismjs/themes/prism-solarizedlight.css")
Tag振り分け
SEO
SEOコンポネントがドキュメントに掲載されています。
Deploy(GitHub-Pages)
プラグイン入れてpushするだけです。
Plugin
エディターのpluginを使えばgraphqlの入力に補間が効くかも・・
感想
簡単簡単と言われているのでQiitaのチュートリアル見ながら30分くらいでいけるのかと思ったのですが、結局ドキュメントをたくさん読むハメになり結構時間がかかってしまいました。
チュートリアルだけではなんで?っていいうお気持ちになるので、余力があればrecipesとcontent-and-dataをみると理解が早まるのでお勧めです。お作法に従って書くのが苦手でなければかなり簡単だと思います。
というか長々とかきましたがお気に入りのstarterから初めて少し変更するぐらいが一番コスパが良いと思います。