Gatsbyノート3(slug/pageの生成)

## 参考
https://bit.ly/2Fq3GcO

### 学習内容

- ブログの詳細ページを作成する

## 1.前提

#### slugとは

> URL（パーマリンク）の末尾の部分を任意の文字列に指定できる機能で、URLの一部になる
> 英数字、アンダースコア、ハイフンからなる短いラベル文字列

#### onCreateNodeとは
https://www.gatsbyjs.com/docs/node-apis/#onCreateNode

Gatsbyで各nodeが生成されるタイミングでnodeにデータを追加し、nodeの再編成をすることができる

- 詳細ページへのslugを生成する
- 作成したslugをnodeに追加する
- gatsby-node.jsに処理を追加する

```sample.js

exports.onCreateNode = ({ node, actions }) => {
  const { createNode, createNodeField } = actions
  // Transform the new node here and create a new node or
  // create a new node field.
}
```

#### basename関数(Slugの抽出)とは
https://nodejs.org/docs/latest-v13.x/api/path.html#path_path_basename_path_ext

```sample.js

path.basename('/foo/bar/baz/asdf/quux.html', '.html');
// Returns: 'quux'
```

#### createNodeFieldとは
https://www.gatsbyjs.com/docs/reference/config-files/actions/#createNodeField

```sample.js

createNodeField({
  node,
  name: `happiness`,
  value: `is sweet graphql queries`
})
```

#### createPages(ページの生成)とは
https://www.gatsbyjs.com/docs/node-apis/#createPages


## 2.gatsby-node.jsでslugとpageの生成


```gatsby-node.js

const path = require('path');

//slugの生成
module.exports.onCreateNode = ({ node, actions }) => {
    const { createNodeField } = actions
    //MarkdownRemarkの場合に、slugを追加する
    if(node.internal.type === 'MarkdownRemark') {
        //node.jsのbasename関数を用いてslugを抽出
        const slug = path.basename(node.fileAbsolutePath, '.md')
        
        //nodeの再編成 createNodeField: https://www.gatsbyjs.com/docs/reference/config-files/actions/#createNodeField  
        createNodeField({
            node,
            name: 'slug',
            value: slug
        })
    }
}

//ページの生成
module.exports.createPages = async ({ graphql, actions }) => {
    const { createPage } = actions
    //ブログ詳細ページ用のテンプレートの取得
    const blogTemplate = path.resolve('./src/templates/blogDetail.js')
    const res = await graphql(`
        query {
            allMarkdownRemark {
                edges {
                    node {
                        fields {
                            slug
                        }
                    }
                }
            }
        }
    `)
    //各ページの生成
    res.data.allMarkdownRemark.edges.forEach((edge) => {
        createPage({
            component: blogTemplate,
            path: `/blog/${edge.node.fields.slug}`,
            context: {
                slug: edge.node.fields.slug
            }
        })
    })
}
```

## 3.各ページでslugを利用する

### index.js

```index.js

import React from "react"
import { graphql, useStaticQuery } from "gatsby"
import Layout from "../components/layout"
import Kv from "../components/kv"
import BlogItem from "../components/blogItem"
import {Container, Row, Col} from 'react-bootstrap'

const IndexPage = () => {
//slug追加
  const data = useStaticQuery(graphql`
    query {
      allMarkdownRemark {
        edges {
          node {
            fields {
              slug
            }
            frontmatter {
              date
              title
              thumbnail {
                childImageSharp {
                  fluid {
                    src
                  }
                }
              }
            }
          }
        }
      }
    }
  `)
  return (
    <Layout>
      <Kv />
      <Container>
        <Row>
          {
            data.allMarkdownRemark.edges.map((edge, index) => (
              <Col sm={4} key={index}>
                <BlogItem
                  title={edge.node.frontmatter.title}
                  date={edge.node.frontmatter.date}
                  src={edge.node.frontmatter.thumbnail.childImageSharp.fluid.src}
//slug追加
                  link={`blog/${edge.node.fields.slug}`} />
              </Col>
            ))
          }
        </Row>
      </Container>
    </Layout>
  )
}

export default IndexPage
```

### blogItem.js


```blogItem.js

import React from 'react'
import { Card } from 'react-bootstrap'
import { Link } from 'gatsby'

function BlogItem({title, date, src, link}) {
    return (
       <Card className="mt-4">
           <Card.Img variant="" src={src} />
           <Card.Body>
                <Card.Title>{title}</Card.Title>
                <Card.Text>
                    {date}
                </Card.Text>
                <Link to={`/${link}`}>see more</Link>
           </Card.Body>
       </Card>
    )
}

export default BlogItem
```