Vue.jsでSSGをする方法を探していたら、Gridsomeという素晴らしいフレームワークを見つけたので、紹介します。
Gridsomeとは?
Gridsomeの特徴は、Jamstackなフレームワークであり、すべてのデータソースに対してGraphQLを使ってアクセスできるところにあります。
たとえば、APIやMarkdownで書かれたファイルなんかをデータソースとして扱い、GraphQLを使ってアクセスできるという事です。このあたりは別途、下で解説します。
あと、プリレンダリングでHTMLを事前に生成しておくので(つまり、SSGなので)、「SEO」にも強い。
私個人の感想としては、GraphQLは人類には早すぎたものだと思っていました。
しかし、いざ使ってみるとこんなに便利なんだ・・・と、考えを改めるほど素晴らしいフレームワークだと感じました。
プロジェクトを作成して起動してみる
npm
とyarn
どちらでも良いのですが、公式ではyarn
をオススメしているので、yarn
でやります。(なお、事前に自分のPCにNode.js
を入れておくこと)
まずは、Gridsome用のCLIをGlobalにインストールして、プロジェクトファイルを作成します。
$ yarn global add @gridsome/cli
$ gridsome create sample-site
プロジェクトファイルが作成できたら、以下コマンドで起動してみましょう。
$ cd sample-site
$ gridsome develop
Gridsomeの更なる特徴を見てみよう
ここからGridsomeの更なる特徴を見てみます。
G-LINK
g-linkタグを使用すると、ページを開いた時に、リンク先のページのデータを取得します。
そのため、リンクを押下する前にページの情報を取得しているので、高速にページを閲覧することが可能です。
# src/layouts/Default.vue
<nav class="nav">
<g-link class="nav__link" to="/">Home</g-link>
<g-link class="nav__link" to="/about/">About</g-link>
</nav>
実際に、ディベロッパーツールから見ても、ページをキャッシュしていることがわかります。
G-IMAGE
デバイスに合わせて、最適化された画像を(解像度や大きさをデバイスに合わせて自動で変更)表示してくれます。
また、g-image内で画像の画質の調整やトリミングも可能です。
# src/pages/Index.vue
<g-image alt="Example image" src="~/favicon.png" width="135" />
imgタグと比較しても、「intrinsicsize」ではないことがわかります。
PagesとTemplate
Gridsomeには、Pages
とTemplate
という概念があります。
Pages
は、言葉通り単一ページのことで、src/pages
ディレクトリ内にvueファイル
を配置することで、動的にURLが生成されます。
公式にも記載がありますが、以下のようにURLが生成されます。
- src/pages/Index.vue = /(フロントページ)
- src/pages/AboutUs.vue = /about-us/
- src/pages/about/Vision.vue = /about/vision/
- src/pages/blog/Index.vue = /blog/
Template
は、コレクション内のノードの単一ページを表します。
コレクションとは、データの塊を表し、その1つ1つをノードと表現します。
ブログサイトなんかを例にすると、投稿データがコレクション、投稿データの中の1つの記事がノードになります。
Markdownファイルから簡単なブログサイトを作ってみる
では、さっそく試してみましょう。
MarkdownファイルをVueコンポーネントを使用してページを作成するには、以下プラグインが必要なので、こちらをインストールします。
$ yarn add @gridsome/vue-remark
次に、プロジェクト設定ファイル(gridsome.config.js
)に上記でインストールしたプラグインを使用するように設定します。
module.exports = {
siteName: 'Sample Site',
templates: {
Tag: "/tags/:id" // タグを使用した時のテンプレートファイル名とURLPath
},
plugins: [
{
use: '@gridsome/vue-remark',
options: {
typeName: 'Post', // GraphQLスキーマのタイプ名
baseDir: './blog', // Markdownファイル配置場所
pathPrefix: '/', // URLPathのプレフィックス
template: './src/templates/Post.vue', // テンプレートファイル名
refs: {
tags: { // タグを使用する
typeName: 'Tag',
create: true // tagsからタグのコレクションを生成
}
}
}
}
]
}
Markdownで書かれたファイルを以下のようにメタデータ
を埋め込んで、いくつか/blog
直下に保存しておきます。
---
title: "OSSのライセンスについて少し調べてみた"
description: "GitHubに落ちているライブラリ。さくっと使えて便利なのですが、ふと「ライセンス」のことも気になったので、調べたことを以下に纏めておこうと思います。 "
tags: ['OSS', 'ライセンス']
date: 2020-07-06
---
## OSSのライセンスについて少し調べてみた
GitHubに落ちているライブラリ。
さくっと使えて便利なのですが、ふと「ライセンス」のことも気になったので、調べたことを以下に纏めておこうと思います。
・・・以下略・・・
次に、投稿データ(コレクション)を表示するページを作ります。
Pages
なので、/src/pages/Blog.vue
で作成します。
<template>
<Layout>
<h1>Blog</h1>
<p>記事の一覧です。</p>
<article class="blog-article" v-for="post in $page.allPost.edges" :key="post.node.id">
<h2>{{post.node.title}}</h2>
<span v-for="tag in post.node.tags" :key="tag.id">
<g-link class="blog-article-glink" :to="tag.path">#{{tag.title}}</g-link>
</span>
<div>{{post.node.description}}</div>
<div>
<g-link :to="post.node.path">本文を読む</g-link>
</div>
</article>
</Layout>
</template>
<page-query>
query ($page: Int) {
allPost (perPage: 5, page: $page) @paginate {
pageInfo {
totalPages
currentPage
}
edges {
node {
id
title
description
date (format: "YYYY/MM/DD")
tags {
id
title
path
}
path
}
}
}
}
</page-query>
<script>
import { Pager } from 'gridsome'
export default {
metaInfo: {
title: 'Blog'
},
components: {
Pager
}
}
</script>
<style>
/* 省略 */
</style>
<page.query>
で囲われたところが、GraphQLのQuery部分です。
Gridsomeは、GraphQLのPlayGroundを起動させることができるので、ここでQueryを実行して、取得するデータ内容を確認できます。
GraphQLを使用することで、ページネーションも楽々に実装できます。
続いて、投稿データの中の1つの記事(ノード)を表示するページを作成します。
Template
なので、/src/templates/Post.vue
で作成します。
<template>
<Layout>
<article>
<VueRemarkContent />
<aside>
<span v-for="tag in $page.post.tags" :key="tag.id">
<g-link class="blog-article-glink" :to="tag.path">#{{tag.title}}</g-link>
</span>
</aside>
</article>
</Layout>
</template>
<page-query>
query Post ($id: ID!) {
post (id: $id) {
title
description
content
path
date (format: "YYYY/MM/DD")
tags {
id
title
path
}
}
}
</page-query>
<script>
export default {
metaInfo() {
return {
title: this.$page.post.title,
meta: [
{ name: 'description', content: this.$page.post.description },
]
}
}
}
</script>
<style>
/* 省略 */
</style>
VueRemarkContent
タグを使用すれば、Markdownの内容(Content)をHTMLに変換して表示できます。
一緒に取得してきたtitle
やdescription
は、メタタグに使用します。
最後に、サイト内共通で使用している/src/layouts/Default.vue
にBlog
へのリンクを追加します。
<g-link class="nav__link" to="/blog/">Blog</g-link>
ここまでで、一通りのページが表示可能ですが、タグの一覧ページも作りたい場合は、/src/templates/Tag.vue
を作成します。
<template>
<Layout>
<h1>#{{ $page.tag.title }}の一覧</h1>
<article class="blog-article" v-for="tag in $page.tag.belongsTo.edges" :key="tag.node.id">
<h2>{{tag.node.title}}</h2>
<div>{{tag.node.description}}</div>
<div>
<g-link :to="tag.node.path">本文を読む</g-link>
</div>
</article>
</Layout>
</template>
<page-query>
query Tag($id: ID) {
tag (id: $id) {
title
belongsTo {
edges {
node {
... on Post {
id
title
description
date (format: "YYYY/MM/DD")
path
}
}
}
}
}
}
</page-query>
<style>
/* 省略 */
</style>
最後に以下コマンドでビルドします。 するとdist
の下にHTMLなどが作られます。
gridsome build
ビルドで作られたHTMLなどをGitHub Pages
やAWS S3
などに配置することで、HTMLを参照できます。
たとえば、GitHub Pages
であれば、gridsome.config.js
に、以下を追加して、ビルドしたものをそのままリポジトリにアップすることでサイトを閲覧できます。
pathPrefix: '/pages/{ユーザー名}/{リポジトリ名}',
outputDir: 'docs',
まとめ
Gridsomeのすごいところは、すべてのデータソースに対してGraphQLを使ってアクセスできるところにあると思います。正直、GraphQLにこんな使い方ができるなんて私は知りませんでした。(考えを改めます)
加えて、SSGなのでSEOに強く、自前でサーバーを用意する必要がないところも素敵です。Nuxt.jsほどモダンなフレームワークを使いたくないって時の選択肢にもなると思います。
是非、みなさんGridsomeの凄さを体験してみてください。
FORK Advent Calendar 2020
18日目 Reactで神経衰弱を作ってみた @ktn
20日目 MediaDevicesとWeb Audio API Vue.jsとThree.js で 音声の波形表示 @shogun-fork