※この記事は私のブログからの転載ですので、こちらも良ければご覧ください〜
はじめに
Gridsomeは様々なデータを静的サイトとして表示することができます。今回はサークルのホームページを作成する時にハマった部分の整理も兼ねて、複数のディレクトリ配下にあるマークダウンファイルを一覧表示する方法を書いていきます。
※この記事に載せているのは私なりの解釈なので、なにかご指摘等ありましたらコメント欄か私のGitHubまで連絡いただけると幸いです。
Gridsome概要
Gridsomeについて超ざっくりと概要を紹介しておきましょう。
GridsomeはVueで作られた静的サイトジェネレータです。似たような技術でReactで作られたGatsbyがあります。静的サイトジェネレータは文字通り静的サイト(HTMLなど)の生成に特化したツールで、比較的データのやり取りが頻繁には発生しないWebサイト(ポートフォリオやブログなど)で使われることが多いです。
GridsomeはGatsbyにインスパイアされて開発されている技術で、その仕組みもGatsbyに似たものになっています。CMSやマークダウンファイルなどのデータがGraphQLデータレイヤーと呼ばれるツールに一時的に保存され、このデータレイヤーから最終的に静的サイトが生成されます。Gridsomeの公式サイトにも、データレイヤーに関して以下のような説明がなされています。
The GraphQL data layer is a tool available in development mode. This is where all of the data imported into a Gridsome project is temporarily stored. Think of it as a local database that helps you work faster and better with your data.
マークダウンデータをインポートする
2020/10/03現在の開発環境は以下の通りです。
Tool | Details |
---|---|
Ubuntu | 20.04.1 LTS x86_64 |
Node.js | v10.19.0 |
Yarn | 1.22.5 |
Gridsome | v0.7.0 |
この作業ではNews
とProducts
についての記事を別々のディレクトリで管理しながら、一括でマークダウンデータをGraphQLデータレイヤーにインポートし、トップページに表示するといった作業を行います。
今回はマークダウンファイルをデータに持っていきたいので、マークダウンファイルをGraphQLデータレイヤーにインポートできるGridsomeプラグインを導入します。@gridsome/vue-remark
というプラグインを、以下のコマンドでぶち込みます。今回はYarn
を使用しています。
$ yarn add @gridsome/vue-remark
yarn add v1.22.5
[1/4] Resolving packages...
[2/4] Fetching packages...
.
.
.
Done in 10.34s.
上記のように@gridsome/vue-remark
の導入が終わったら、gridsome.config.js
に以下のような書き込みを行います。
//gridsome.config.js
module.exports = {
siteName: 'Gridsome',
plugins: [
{
use: '@gridsome/vue-remark',
options: {
typeName: 'NewsPost', // 必須。GraphQL上で扱う型定義
baseDir: './posts/news', // 記事となるmarkdownファイルを置くディレクトリ
pathPrefix: '/news', // URLになるパス。必須ではない。
template: './src/templates/NewsPost.vue' // 記事ページのVueコンポーネントファイルの指定
}
},
{
use: '@gridsome/vue-remark',
options: {
typeName: 'ProductPost',
baseDir: './posts/products',
pathPrefix: '/products',
template: './src/templates/ProductPost.vue'
}
}
]
}
これらの書き方は@gridsome/vue-remarkにしっかりと載っています。この部分は以下のように説明することができます。
- マークダウンファイルをデータとして利用するには、マークダウンファイルを入れておくディレクトリがそれぞれのGraphQL上の型1つにつき1つ必要。
-
News
とProduct
のそれぞれに対して、GraphQL上の型・格納するディレクトリ・パス・テンプレートファイルの指定を行っている。 - 先程導入したプラグインを、
News
とProduct
のそれぞれに対して適用させている。
このgridsome.config.js
が、Gridsomeのプロジェクトのデータ設定になるので、ここは非常に重要となります。この設定一つでデータを読み込めなかったりするので、しっかり記述しましょう。
データの定義が完了したので、次はデータとなるマークダウンファイルを格納するディレクトリを作成します。
mkdir posts/news
mkdir posts/products
2つのnews
ディレクトリとproducts
ディレクトリの中には、以下のような形式でマークダウンファイルを作成します。両方のディレクトリにこの形式のマークダウンファイルがないとエラーを吐きますので、2つともファイルを作成しておきましょう。
---
title: "成功していればここにタイトルが表示されます"
---
## これは最初の投稿です
マークダウンで記事一覧を表示することかできたぞ!やったぜ!
テンションあがるなァ^〜
こうしてマークダウンファイルを作成しました。---
で囲まれた部分は「フロントマター」と呼ばれ、各マークダウンファイルのメタデータの定義・設定を記述できます。フロントマターを記述すると、先程のプラグインによってメタデータがGraphQLデータレイヤーにインポートされます。具体的には
- title: マークダウンファイルで
title
を記述した部分 - content: 記事本文
の2つに加え、
- id: 各マークダウンファイルに紐付けられている一意の文字列
の3つの情報がインポートされています。以降は、これらデータレイヤー上のデータを必要に応じてQuery(日本語で「照会・問い合わせ」)していきます。
投稿一覧ページを作る
次に各ページにGraphQLを記述して、データレイヤー上にインポートされたデータから必要なデータを照会していきます。Index.vue
に記述されているデフォルトのコードにGraphQLのデータを照会する部分の項目を追記していきます。なお、<script>
と<style>
は本題から外れますし、デフォルト値のままなので割愛しています。
<!-- Index.vue -->
<template>
<Layout>
<h1>Hello, world!</h1>
.
.
.
<!-- 以下を記述-->
<div>
<h1>News</h1>
<g-link
v-for="news in $page.allNewsPost.edges"
:key="news.node.id"
:to="news.node.path">
<h2>{{ news.node.title }}</h2>
</g-link>
</div>
<div>
<h1>Products</h1>
<g-link
v-for="product in $page.allProductPost.edges"
:key="product.node.id"
:to="product.node.path">
<h2>{{ product.node.title }}</h2>
</g-link>
</div>
</Layout>
</template>
<!-- 以下を記述 -->
<page-query>
query{
allNewsPost{
edges{
node{
id //各マークダウンファイルに対応する一意の文字列
title //フロントマター上で記述したメタデータ
path //プラグインによって設定されたパス
}
}
},
allProductPost{
edges{
node{
id
title
path
}
}
}
}
</page-query>
ここでは以下のような挙動を意図しています。
-
<page-query>
内では、gridsome.config.js
で定義したGraphQLデータ型を参考に、all[型名]
という名前で指定した型の持つすべてのデータを指定します。今回ではNewsPost
型とProductPost
型のデータを全て参照するallNewsPost
とallProductPost
を用いています。このページでは、これらのデータを参照して利用していきます。 -
g-link
内では以下の操作を行っています-
$page
でこのページを範囲として一括で参照しておいて、news in $page.allNewsPost.edges
でNewsPost
型データのedge
以下にあるデータ群にnews
と名付け、v-for
で繰り返し表示しています。 -
:key
ではv-for
をかける際の個々のマークダウンファイルの識別を、:path
ではブラウザ表示した際の個々のマークダウンファイルに対応したパスの受け渡しを行っています。 -
news.node.title
で、マークダウンファイルのフロントマターのtitle情報をデータレイヤー上から持ってきて表示しています。このnews
とは、先程g-link
タグ内でnews
と名前をつけたデータ群です。
-
これらはNews
を対象としていますが、全く同様の操作をProduct
でも行えば大丈夫です。この操作をすることで、トップページに記事のタイトルが一覧として表示されます。
最後に、マークダウンファイルを表示する時に利用するVueファイルを作成していきます。./src/templates
配下にNewsPost.vue
というVueファイルを作成します。
<!-- NewsPost.vue -->
<template>
<article>
<h1>{{ $page.newsPost.title }}</h1>
<VueRemarkContent/>
</article>
</template>
<page-query>
query NewsPost ($id: ID!) {
newsPost(id: $id) {
title
content
}
}
</page-query>
ここでは以下の操作を行っています。
- 先程同様、データレイヤー上のデータから
<page-query>
内でデータを参照します。今回ではNewsPost
型のマークダウンファイルデータのうちnewsPost
と名前をつけてid: $id
でid
を手がかりにすると指定し、各id
に対応したマークダウンファイルのtitle
(記事のタイトル:フロントマター内の記述)とcontent
(記事本文:フロントマターより下にある記述)を照会しています。 -
$page.newsPost.title
で同様にtitle
データを、<VueRemarkContent/>
でcontent
データを表示します(<VueRemarkContent/>
はプラグインで定められた記述です)。
ここでちょっとGraphQLの動作について掘り下げてみましょう。上記のNewsPost.vue
のpage-query
で、新たに$id: ID!
なる記述が出てきたかと思います。
GraphQLの公式ドキュメントには、このような記述がなされています。
Variables
So far, we have been writing all of our arguments inside the query string. But in most applications, the arguments to fields will be dynamic: For example, there might be a dropdown that lets you select which Star Wars episode you are interested in, or a search field, or a set of filters.
It wouldn't be a good idea to pass these dynamic arguments directly in the query string, because then our client-side code would need to dynamically manipulate the query string at runtime, and serialize it into a GraphQL-specific format. Instead, GraphQL has a first-class way to factor dynamic values out of the query, and pass them as a separate dictionary. These values are called variables.
この$id: ID!
では、ID!
型の変数$id
を定義しています(!
はNull許容)。NewsPost.vue
はファイル自体は1つのVueコンポーネントですが、実際に表示するマークダウンファイルは複数個あるうちの1つとなります。そしてこのコンポーネントが指定するGraphQL上のデータも、複数あるマークダウンのうちの1つのみとなります。つまり、コンポーネントに渡されるデータIDが動的なものなります。このように対象となるデータが逐一変化する時のGraphQL上のデータは、単語の頭に$
をつけて「変数」として宣言しなければなりません。これに注意して、NewsPost.vue
のGraphQLを記述する必要があるんですね〜。
先のIndex.vue
が表示の対象としていたマークダウンファイルは、ディレクトリ配下に存在する「全ての」マークダウンファイルでしたので、それらが提供するデータをall
を付けることで一括して指定していましたね。それの1個ずつバージョンと考えています。
Product
でも全く同様のことを記述していけばよいです。その際は、照会するデータ型がNewsPost
型とProductPost
型で違うので、別途ProductPost.vue
のようなVueテンプレートを作成していくと良いでしょう。
所感
GraphQLが具体的にどんなことをやっているのかを理解できたのはデカいと思います。というか、内容自体がGridsomeというよりGraphQLについての記事になってしまった気がします。GraphQlを除けば、Gridsomeは実質VueみたいなものでしたねKing of 安直。いつの日か断念していたGatsby開発も、GraphQLの挙動を理解した状態でリベンジできるんじゃねとちょっと思いますた。