Hero img
Gatsbyテンプレから始める

Gatsbyにmarkdownとの奮闘記

gatsby-starter-blogをいじってみてmarkdownの理解を深める。ために頑張ってみた奮闘記です。参考になるものはあまりないと思う。


目次

  • 目標
  • Markdownの実装
  • gatsby-starter-blogを使う
  • ダウンロードしてみる
  • ソースの確認
  • 分析してみる
  • gatsby-source-filesystem
  • markdown(md)を使用するには
  • ページの作成
  • mdのページが作られる流れ
  • createPage
  • blog-post
  • pageQuery
  • pageQueryを解決する
  • まとめ

目標

markdownを理解する。
gatsby-starter-blogをいじってgatsbyのmarkdownとは何だ?
markdownを実装するにはどうすれば良いのか?
を理解するために戦った傷跡です。参考にはならないと思いますが...
markdown解説はこちらを確認ください。

Markdownの実装

markdown実装と言いたいがとりあえず理解していないと実装できないので、サンプルソース今回確認することにした。
このテンプレを使って始めてみました。

gatsby-starter-blogを使う

ダウンロードしてみる

公式ページからダウンロード してみる

npx gatsby new gatsby-starter-blog https://github.com/gatsbyjs/gatsby-starter-blog

ソースの確認

githubソースコードを見てみました。 ソースで必要な部分を一部抜粋しました。

gatsby-node.js
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.createPages = async ({ graphql, actions, reporter }) => {
  const { createPage } = actions
  const result = await graphql(
    `
      {
        allMarkdownRemark(
          sort: { fields: [frontmatter___date], order: ASC }
          limit: 1000
        ) {
          nodes {
            id
            fields {
              slug
            }
          }
        }
      }
    `
  )
  const posts = result.data.allMarkdownRemark.edges
  if (posts.length > 0) {
    posts.forEach((post, index) => {
      const previousPostId = index === 0 ? null : posts[index - 1].id
      const nextPostId = index === posts.length - 1 ? null : posts[index + 1].id

      createPage({
        path: post.fields.slug,
        component: blogPost,
        context: {
          id: post.id,
          previousPostId,
          nextPostId,
        },
      })
    })
  }
}

分析してみる

分かったことは、 graphqlとはいうものを使っているらしい。 graphqlの登録、参照については ここの解説がとても参考になりました。ありがとうございます。

重要なのはgatsby-source-filesystemのプラグインであることが判明 http://localhost:8000/___graphql で確認するといいらしい。

gatsby-source-filesystem

まずはgatsby-config.jsを確認してみる。

gatsby-config.js
 {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/content/blog`,
        name: `blog`,
      },
    },

よくわからないがこれでcontent/blogの中を登録しているようだ。
確認をするにはdevelopで起動させてhttp://localhost:8000/___graphqlにアクセスしたら確認ができる。

gatsby develop
graphiql-node-absolutePath

試しにcomponentsを追加してテストする

gatsby-config.js
  plugins: [
    `gatsby-plugin-image`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/content/blog`,
        name: `blog`,
      },
    },
    /**これを追加した**/
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/src/components`,
        name: `components`,
      },
    },
    /**ここまで追加した**/

そして同様にabsolutePathを確認するとcomponetsの中身も追加あれていることが分かった。 graphqlで値を取得できるところまで確認ができた。

markdown(md)を使用するには

gatsby-transformer-remarkのプラグインを使うとできるみたいだが、mdはgraphqlを使用している。そのためgraphqlで取得できていないファイルはmarkdownで記事を作成できない。
とりあえずプラグインを追加する。

gatsby-config.js
resolve: "gatsby-transformer-remark",

このプラグインはgatsby-source-filesystemdで登録した、フォルダー内のmdファイルを自動で検知して、読み込んでくれる。そのためgraphqlで取得できている。尚且つmdファイルである場合mdを実装できる。
テストをしてみる。先ほどcomponentsを追加したのでその中にtest.mdを追加してみた。内容は適当に記入する。
addingtestmd

またgatsby developを実行して確認してみる

query MyQuery {
  allMarkdownRemark {
    nodes {
      fileAbsolutePath
    }
  }
}
mdfileabspath

正常にtext.mdが追加されたことが確認できた。

ページが追加されていることも確認ができる addingmd_pages

ページの作成

mdファイルが読み込まれることが分かったので当ページにも追加してみたがページが増えなかった。 ここでページを作成しているのは実はgatsby-node.jsであることがここで判明する。 createPage({})があって、これがページが作成している。

gatsby-node.js
if (posts.length > 0) {
    posts.forEach((post, index) => {
      const previousPostId = index === 0 ? null : posts[index - 1].id
      const nextPostId = index === posts.length - 1 ? null : posts[index + 1].id

      createPage({
        path: post.fields.slug,
        component: blogPost,
        context: {
          id: post.id,
          previousPostId,
          nextPostId,
        },
      })
    })
  }

それではこのposts.forEachとは何かとさかのぼると、最終的にこれの事でした。
console.logを入れてgatsby developを実行してみる

gatsby-node.js
  const result = await graphql(
    `
      {
        allMarkdownRemark(
          sort: { fields: [frontmatter___date], order: ASC }
          limit: 1000
        ) {
          nodes {
            id
            fields {
              slug
            }
          }
        }
      }
    `
  )
  //省略
  const posts = result.data.allMarkdownRemark.nodes;

gatsby developを実行してみると以下のようなログが表示される。 gatsbydevelop-md-cmd いやいや「data.allMarkdownRemark.nodes」ってどこからきた?
じつはこれはhttp://localhost:8000/___graphqlと同様な事を行っている。 graphqlで同じコードを実行してみると

query MyQuery  {
        allMarkdownRemark(
          sort: { fields: [frontmatter___date], order: ASC }
          limit: 1000
        ) {
          nodes {
            id
            fields {
              slug
            }
          }
        }
      }

以下の結果が返って来る

{
  "data": {
    "allMarkdownRemark": {
      "nodes": [
        {
          "id": "7cdce3cf-71fc-573b-bb84-03526189160f",
          "fields": {
            "slug": "/hello-world/"
          }
        },
        {
          "id": "f5858bf6-a8f4-5d7e-9bf6-78faa3b44d1d",
          "fields": {
            "slug": "/my-second-post/"
          }
        },
        {
          "id": "eac236df-9f92-5e44-ad6e-0240b0f2e9ff",
          "fields": {
            "slug": "/new-beginnings/"
          }
        },
        {
          "id": "0ac780b5-e106-53fb-b828-41553a44205b",
          "fields": {
            "slug": "/test/"
          }
        }
      ]
    }
  },
  "extensions": {}
}

つまりposts.forEachのpostsはnodes:[{ "id": "7..."}...省略]であることが分かった。

mdのページが作られる流れ

ここまでを整理すると

  1. 1.gatsby-source-filesystemでgraphqlに登録する。
  2. 2.graphql内のmdファイルをgatsby-transformer-remarkが検知して新たにallMarkdownRemarkのgraphqlを登録する。
  3. 3.gatsby-node.jsでallMarkdownRemarkを取得して、createPageを実行する。
    上記のようにbuild時にgraphqlが登録され、gatsby-node.jsが実行され、ページを作成している。

createPage

createPageの公式ページを確認してみる。 簡潔に

  • path : urlのサブディレクトリ
  • component : ページのテンプレート
  • context : 引数でこの値を渡せる
    最低pathとcomponentが必要になる。
    ここまで来たらサンプルと同じように、componentを用意して、forEachでcreatePageを実行すればいいので簡単にだと思ったのだが...次はcomponentで躓いてしまった。componentは今回のサンプルでは「./src/templates/blog-post.js」です。

blog-post

createPageまでは理解できた、あとはblog-post.jsだけだ。 一部抜粋

blog-post.js
const BlogPostTemplate = ({
  data: { previous, next, site, markdownRemark: post },
  location,
}) => {
//省略
//26行目
        <section
          dangerouslySetInnerHTML={{ __html: post.html }}
          itemProp="articleBody"
        />
//省略
}
//75行目
export const pageQuery = graphql`
  query BlogPostBySlug(
    $id: String!
    $previousPostId: String
    $nextPostId: String
  ) {
    markdownRemark(id: { eq: $id }) {
      id
      excerpt(pruneLength: 160)
      html
    }
    }
`

コードを確認するとmdからhtmlのパースはされている用で、dangerouslySetInnerHTML={{ __html: post.html }}でページに反映できるよう。 export const pageQuery = graphqlを使用してる 正直このpageQueryでかなり躓いてgatsbyを投げ出したくなった。

pageQuery

pageQueryで理解できなかったこと。

  1. 1.そもそもこれ必要?
  2. 2.graphqlをあまり理解していない。
  3. 3.pageQueryはどこで利用されているの?
  4. 4.$id: String!のidってどこからやってきたの
  5. 5.markdownRemark(id: { eq: $id })のidってなんで必要なの?
  6. 6.そもそもコードが長い見たくない。

pageQueryを解決する

pageQuery必要性

必要だった。これを記述しないとmdで記入したものをページに反映できない。 ただの空のページが作成されてしまう。

graphqlの理解

gatsbyを使用する上でgraphqlを理解しないとやっていけない登録は置いといてせめて、読み込みを理解しないといけない。http://localhost:8000/___graphql で値は確認できるので使いこなさないと今後厳しい

pageQueryの使用用途

export const pageQuery = graphqlをすることでmdの値、その他の必要な情報を全て渡すことができる。逆に言うとここで指定しないと、何も値を読み込みできない。

const BlogPostTemplate = ({
  data: { previous, next, site, markdownRemark: post },
  location,
})

サンプルソースでは値を細かく決めているのでとりあえず全てを取得できるようにコードを変えて、分解すると理解できるかもしれない。

const BlogPostTemplate = (props) => {
const data = props.data;
const previous = props.data.previous;
const next = props.data.next;
const site = props.data.site;
const post = props.data.markdownRemark.post;
const location = props.location;
}

idについて

idってなんで必要?
idが無いと全てのmarkdownの記事を取得するため(厳密には多少異なる)特定のmarkdownファイルをidでフィルタ―を行いmarkdown内の値を取得している。そのためidでなくても特定のmarkdownファイルを指定できるのであれば問題ない。「fileAbsolutePath」絶対パスとかを使うこともできる。
idはgatsby-node.jsで指定してblog-post.jsに渡している。

gatsby-node.js
      createPage({
        path: post.fields.slug,
        component: blogPost,
        context: {
          id: post.id,
          previousPostId,
          nextPostId,
        },
      })

もしくはgatsby-node.jsで渡すblog-post.jsに渡す情報を全て追加すればidは不要となる。 まぁ、結局記述内容が増えて全て渡すと管理しづらい。build時間は短縮されるのかな?id無しの方が良かったりして?

gatsby-node.js
      createPage({
        path: post.fields.slug,
        component: blogPost,
        context: {
          id: post.id,
          previousPostId,
          nextPostId,
          body: html,
          title: title,
          date: post.frontmatter.date
        },
      })

まとめ

markdownを実装するのに物凄く苦労しました。最終的にmarkdownでページが作成される流れは

  1. 1.gatsby-source-filesystemでgraphqlに登録する。
  2. 2.graphql内のmdファイルをgatsby-transformer-remarkが検知して新たにallMarkdownRemarkのgraphqlを登録する。
  3. 3.gatsby-node.jsでallMarkdownRemarkを取得して、createPageを実行する。 その際markdownのidを渡している。
  4. 4.idでフィルタリングを行いmarkdownの値等必要な情報をpageQueryが行って、取得している。
  5. 5.pageQueryで指定してた値を暗黙的にpopsで取得しているのでそれらを利用する。
  6. 6.popsで得られた値を使ってHTMLの中身を作成していく。
    流れを一度分かってしまえば理解が速くなると思います。この流れを理解するのに本当に苦労しました。

関連記事

コメント

コメントを書く

質問や、間違いがありましたら、お気軽にどうぞ

※お名前とコメントの内容を入力してください。
※全てこのブログ上に公表されます。
※許可なく管理者によって修正・削除する場合がございます。 詳しくはプライバシーポリシーを参照ください