あいつの日誌β

働きながら旅しています。

VuePress v1.0-alpha で Blog を作る

v1.vuepress.vuejs.org

あらすじ

VuePress の default theme が美しいのでこれで Blog を書きたいと思いました。

VuePress とは

コンセプトは以下2点です。

  • Vue component を使用した Theme を扱う最小構成の静的サイトジェネレーター
  • 技術的な文章を書くために最適化された default theme を提供する

一度読み込まれたHTMLは、それ以降 Single Page Application のように振る舞うためレンダリングは高速ですが、最初のHTMLにコンテンツを書き出しているので SEO との親和性も高い CMS です。

この振る舞いは nuxt generategatsby build と非常に良く似ています。

使われている技術と似ているサービス

Vue, Vue Router, webpack が使用されています。 

VuePress を使ってドキュメントを書く

VuePress を Default Theme を使って利用する場合、必要な作業は以下の通りです。

  • defaultTheme の振る舞いを .vuepress/config.js で設定します。
  • Markdown 形式のテキストファイルを追加します。

Blog 化するために必要な事

VuePress を初期状態で使う場合、ナビゲーションの機能が不十分です。今回私が VuePress を Blog 向けに使う為に、以下の2点の作業を行いました。

  • BlogIndexPage を追加し、記事一覧を表示する
  • sidenav に各記事へのリンクを動的に生成する

BlogIndexPage を追加

.vuepress/components/PostIndex.vue を追加します。

<template>
  <div>
    <div v-for="post in posts">
      <h3>{{ post.frontmatter.title }}</h3>
      <p>{{ post.frontmatter.description }}</p>
      <p><router-link :to="post.path">Read more</router-link></p>
    </div>
  </div>
</template>

<script>
export default {
  computed: {
    posts() {
      return this.$site.pages
        .filter(x => x.path.startsWith('/posts/') && !x.frontmatter.post_index)
        .sort((a, b) => new Date(b.frontmatter.date) - new Date(a.frontmatter.date));
    }   
  }
}
</script>

現状だと Default Theme の Header や Sidebar などを override しづらい構成になっています。 自由にカスタマイズすることも可能なのですが eject する必要があります。まだ VuePress が alpha 版ということもあるので Theme に対するカスタマイズは避けたほうが賢明かもしれません。

ただし、上記のように Page 内に新たに Component を追加する場合は割と簡単です。

追加した PostIndex component を利用して記事一覧ページを作成するには /posts/README.md を以下のようにすればOKです。この例では記事一覧ページの幅を広げたいので pageClass を指定しています。 指定した class の CSS.vuepress/styles/index.styl に記述をすれば適用できます。

---
post_index: true
pageClass: post-index
sidebar: false
---
<PostIndex />

Sidebar を Blog 風にカスタマイズする

.vuepress/config.js に以下のように記述をすると Sidebar のカスタマイズが可能です。ただし Blog の Post のように何度も記事が追加される場合、都度このようにハードコーディングを行うのは煩雑です。

module.exports = {
  themeConfig: {
    sidebar: [
      { 
        title: '2018',
        children: ['/posts/2018/xxxx', '/posts/2018/yyyy']
      },
      {
        title: '2017',
        children: ['/posts/2017/xxxx', '/posts/2017/yyyy']
      }
    ]
  }
}

Life Cycle の中で ready 関数が Hook されているので config を動的に変更することができます。私は年ごとに記事をまとめたかったので以下のようにしました。

module.exports = (options, ctx) => {
  return {
    ready() {
      const { pages, themeConfig } = options

      const items = _.orderBy(pages, 'frontmatter.date').reverse()
      const sidebar = []
      const posts = items.filter(p => p.frontmatter.title && p.frontmatter.type !== 'talk')
      
      for (let i = 0; i < posts.length; i++) {
        const post = posts[i]
        const m = moment(post.date)
        const year = m.year()
        if (!sidebar[year]) {
          sidebar[year] = []
        }
        sidebar[year].push(post.path)
      }
      
      const years = Object.keys(sidebar)
      themeConfig.sidebar = years.sort().reverse().map(year => {
        return {
          title: year,
          children: sidebar[year].map(item => item)
        }
      })
    }
  }
}

まとめ

というわけで Default Theme に少しだけ Component を追加していますが、このようにして Blog 風にカスタマイズすることができました。VuePress は非常に美しいデザインで、技術的なドキュメントを作成するのにぴったりだと思います。

以下、VuePress を使った私の Blog です。海外進出を目論んでいるので英語で書いていますが*1 VuePress で書き出されているのでよかったらご覧になってください。

Blog: https://okamuuu.com/

ソースコード: https://github.com/okamuuu/okamuuu.com

*1:文法があっているかはわかりません