GatsbyJS と TypeScript でブログを作成して公開する(2)
~ GatsbyJs で gatsby-starter-blog を使ったサイトの作成 ~
今回は実際にGatsby CLI
を使って、サイトを生成してみたいと思います。
※環境構築については、前回の投稿をご覧ください。
Gatsby CLI で新規のサイトを作成する
今回はgatsby-starter-blog
というスターター(後述)を使用します。
Mac ならターミナルを、Windows なら Power Shell(管理者権限) を開いて、
$ gatsby new my-blog-starter https://github.com/gatsbyjs/gatsby-starter-blog
※これ以降は、Windows・Mac にかかわらず、上記の$
マークでコマンドの開始を表現します。
※Windows ではおそらく管理者権限で起動が良いかと思います。 通常の PowerShell で実行した場合に、開発サーバー起動のコマンドが失敗することがありました。
コマンドの意味ですが、
gatsby new [任意のプロジェクト名] [StarterのURL]
という形式になっており
-
new
・・・新しいプロジェクトを作成するコマンド -
[任意のプロジェクト名]
・・・好きな名前を指定します。この名前で新規フォルダーが作成されます。(my-blog-starter) -
[StarterのURL]
・・・スターターの URL、指定しなくても良い
スターターとは
公式では、
Starters are boilerplate Gatsby sites maintained officially, or by the community.
と紹介されています。
公式や、コミュニティーが開発した、テンプレートのようなもので、
これを利用することで、あらかじめ必要な設定や、ソースコードのテンプレートなどが含まれた状態で、プロジェクトを生成することができます。
一から自分で必要なライブラリや依存関係を調べてインストールしていくのは骨のを折れる作業です。
自分の目的に合ったスターターを見つけて指定すれば、これらを一発でやってくれるので相当な時間短縮になります。
また、それらのライブラリを使ってある程度のコードが書かれていますので、
個人的にはライブラリの使い方の勉強にもなりました。
スターターは Gatsby のサイトで探すことができます。
2020/11/22 時点で 456 あるようです。
解説が長くなりましたが、上で実行したコマンドで、my-blog-starter
というフォルダが作成されているはずです。
Development Server を立ち上げて、ローカルでブログを確認する
次に、先ほど作成したプロジェクトを起動して確認してみます。
まず、プロジェクトのルートディレクトリに移動します
$ cd my-blog-starter
そして、サーバーを立ち上げます
$ gatsby develop
ブラウザを開いて、http://localhost:8000
にアクセスすると、
画像のようなサイトが立ち上がっているのが確認できます。
初めての Gatsby サイトができました!
記事を追加してみる
gatsby-blog-starter には、Markdown から記事を生成するためのプラグインがデフォルトで入っています。
デフォルトで表示されている記事も Markdown から生成されています。
以下のディレクトリに入っているのがそれですね。
> content
> assets
> blog
> hello-world
index.md
salty_egg.jpg
> my-second-post
index.md
> new-begginings
index.md
試しに、一つ追加してみます。
content/blog
配下にtest-post
というファイルを作って、
そこにindex.md
というファイルを新規作成します。
> content
> assets
> blog
...
> test-post
index.md
index.md
は以下のようにします
---
title: Test Post
date: "2112-09-03"
description: "This is a test post."
---
# これはテストです!
テストテスト
ファイルを保存して、先ほど開いていたhttp://localhost:8000
をみると、
記事の一覧に一つ記事が増えていることが確認できると思います。
追加された記事はリンクになっていますので、クリックしてみると内容が確認できます。
記事を追加してからgatsby develoop
コマンドを実行しなおす必要もありません。
Gatsby はホットリロードの機能がついており、記事だけでなく
React.js で実装するその他のデザインなどもすぐに反映されます。
といっても調べると様々な理由で結局コマンド叩きなおすことが多いみたいですが。。。
ともあれ GatsbyJS はこういった開発を効率よく行う機能が簡単に使えるのがいいところです。
Markdown から記事が生成される仕組み
GatsbyJS において、動的にページを生成する場合に用いられるのが、
gatsby-node.js
ファイルです。
こちらはビルドプロセスで呼ばれる API になっており、
動的なページ生成、GraphQL へのノードの追加、ビルドライフサイクル中のイベントをキャッチして処理を行ったりといったことが可能になります。
新しいページを作成するには 2 つのステップがあります。
- ページのパス(slug)をつくる
- ページそのものを生成する
という工程を踏む必要があり、これを行っているのがgatsby-node.js
ということです。
ちなみにslug
というのはウェブのアドレスの一部で、例えば、
https://kohsuk.tech/posts/no-one
というアドレスがあった場合
/posts/no-one
の部分のことをさします。
ページそのものと、そこに到達するための URL の生成が必要というイメージです。
onCreateNode
とcreatePage
gatsby-node.js
内でこれを行っているのが、onCreateNode
とcreatePage
です。
前者から見ていきます。
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions;
if (node.internal.type === `MarkdownRemark`) {
const value = createFilePath({ node, getNode });
createNodeField({
name: `slug`,
node,
value,
});
}
};
Gatsby は追加された外部のデータをnode
として扱います。
onCreateNode
はこのnode
が作られた際に呼ばれる関数です。
MarkdownRemark
という文字列が見えますね。
Markdown ファイルは、gatsby-transformer-remarkというプラグインがパースし、
node
にそのデータを追加してくれています。
MarkdownRemark
はこのプラグインで作られたデータだということです。
このnode
からファイル名を取得し、それを利用してslug
を生成、node の field として追加、
というのがここでの処理です(createNodeField
)。
続いて、createPage
です。
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions;
// Define a template for blog post
const blogPost = path.resolve(`./src/templates/blog-post.js`);
// Get all markdown blog posts sorted by date
const result = await graphql(
`
{
allMarkdownRemark(
sort: { fields: [frontmatter___date], order: ASC }
limit: 1000
) {
nodes {
id
fields {
slug
}
}
}
}
`
);
if (result.errors) {
reporter.panicOnBuild(
`There was an error loading your blog posts`,
result.errors
);
return;
}
const posts = result.data.allMarkdownRemark.nodes;
// Create blog posts pages
// But only if there's at least one markdown file found at "content/blog" (defined in gatsby-config.js)
// `context` is available in the template as a prop and as a variable in GraphQL
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,
},
});
});
}
};
ここでは主に2つのことが行われています。
- GraphQL でマークダウンの一覧を取得する
- 取得した一覧からページを生成する
ここで注目すべきは、先ほどnode
の field に追加したslug
を GraphQL で取得していることです。
このようにnode
に追加したデータは GraphQL でアクセスすることができます。
ページ全体の生成
マークダウンを拾っている部分は何となくわかりましたが、
マークダウンは記事の中身であって、その他のヘッダーやフッターなどは別に定義しているはずです。
createPage
する際にこれに使うコンポーネントのパスが指定されていますが、
これが、ページを作る際のテンプレートになっています。
createPages
の関数内で上部で指定されている./src/templates/blog-post.js
ですね。
このblog-post.js
ファイルには、React のコンポーネント、GraphQL が定義されています。
ファイル下部で定義された GraphQL のクエリをよく見てみますと、
引数として、id
, previousPostId
, nextPostId
を受け取ることがわかります。
export const pageQuery = graphql`
query BlogPostBySlug(
$id: String!
$previousPostId: String
$nextPostId: String
) {
...
お気づきかもしれませんが、これは先ほどのgatsby-node.js
の createPage でcontext
として指定されています。
createPage({
path: post.fields.slug,
component: blogPost,
context: {
id: post.id,
previousPostId,
nextPostId,
},
})
つまり、実際のページ全体の生成は、
createPage
が、① ページのパス、ページに使用する② コンポーネントのパス、そのページで使う③GraphQL の引数を指定して呼び出され、
実際の記事データは、blog-post
で引数として渡されたid
から GraphQL で取得,
そのデータをコンポーネントに注入してレンダリング、というようになっているんですね!
今回はここまでです。
これからも精進して行きます!