キャプチャ

ブログ記事をマークダウンで書くようにしたら幸せになった話

Vue.js が好きです。

Laravel Vue.js を連携させたプロジェクトが大好きです。

そんな僕は Vue.js で作成した個人サイトを運営しており、その中のコンテンツの1つにブログが存在します。

そんなブログ記事を今まで愚直に html で書いてきました

ツラい。

とてもツラい。文章を書くのがメインのはずなのに 1/4 ぐらいがタグになる。

そこで解決策として、マークダウンを使うのはどうかと考えました。

処理の流れは以下の通り。

1.axios で API を叩いてブログ記事の文字列を持ってくる
  (このときのブログ記事はマークダウンで書かれたもの)
2.拾ってきたマークダウンを marked.js で html に変換する
3.変換した html にはたまにプログラムコードが乗っかっているので highlight.js で良い感じにシンタックスハイライトを付与する

とりあえず Vue CLI 3 で作成した空の状態のテンプレートがこちらです。

<template>
<div id="app">
</div>
</template>

<script>
export default {
  name: 'app'
}
</script>

<style>
</style>

ここにとりあえずマークダウンが描画されるようにします。

data にマークダウンの文字列を入れるための変数を用意し、テンプレート内にその中身を描画するためのタグを作ってあげます。というのが以下のような感じ。

<template>
<div id="app">
  <div v-html="detail"></div>
</div>
</template>

<script>
export default {
  name: 'app',
  data () {
    return {
      detail: 'これは文章ですよ。'
    }
  }
}
</script>

<style>
</style>

実行するとこのようになります。

ちゃんと detail の中身が描画されました。

次に、 detail の中身を外部ファイルを使って設定します。ここで axios の出番となりますね。

axios をインストールするために以下のコマンドを実行します。

# npm の場合
npm install axios --save

# yarn の場合
yarn add axios

外部ファイルは public/static というフォルダを用意して、その中に作っておきます。今回は sample.md というタイトルで下記のような内容にしました。

これは**テスト文章**です。

# 見出しはこんな感じで

[リンク](https://nononotyaya.net/)はこんな感じ。

```javascript
const str = "複数行に渡る";
let hoge = "プログラムコードは";
const huga = "こんな感じ";
```

* リストとかも
* 作れちゃうのだ

おしまい。

とりあえずマークダウンの記法に則っていろいろと書きました。ちなみに、これを html で書くとこんな感じになります。

<p>
    これは<strong>テスト文章</strong>です。
</p>

<h1>見出しはこんな感じで</h1>

<a href="https://nononotyaya.net/" target="_blank">リンク</a>はこんな感じ。

<pre><code>const str = "複数行に渡る";
let hoge = "プログラムコードは";
const huga = "こんな感じ";</code></pre>

<ul>
    <li>リストとかも</li>
    <li>作れちゃうのだ</li>
</ul>

<p>
    おしまい。
</p>

うーん、 p タグが乱立していると読み返しづらいですね。リンクも文字数かさばっちゃってパッと見では誤字に気づけなさそうです。

ではファイルを取得しましょう。

<template>
<div id="app">
  <div v-html="detail"></div>
</div>
</template>

<script>
import axios from 'axios';

export default {
  name: 'app',
  data () {
    return {
      detail: 'これは文章ですよ。'
    }
  },
  methods: {
    async getDetail () {
      // 取得するファイルのパス
      const url = '/static/sample.md';
      try {  // 成功したら結果を渡す
        return (await axios.get(url)).data;
      } catch (e) {  // 失敗したらエラーコードを渡す
        throw e.response.status;
      }
    }
  },
  mounted () {
    this.getDetail()
      .then(result => this.detail = result)
      .catch(result => alert(result));
  }
}
</script>

<style>
</style>

axios を import して、 async と await を使った getDetail() という関数を作り、 axios を通してファイルを手に入れるような処理を組みました。そしてページ読み込み時に呼び出される mounted() にて実行されるように書いておきます。読み込み成功時には変数である detail の中身を書き換えるようにもしています。

結果は以下のような感じ。

ちゃんと sample.md の内容がそのまま描画されています。が、そのまま過ぎます。ちゃんと html になおしてもらいたいですね。

次に marked.js の出番です。とりあえずインストール。

# npm の場合
npm install marked --save

# yarn の場合
yarn add marked

axios のように marked を import して、変換したいマークダウンの文字列を marked() という関数に放り込むだけです。

処理は以下の通り。

<template>
<div id="app">
  <div v-html="detail"></div>
</div>
</template>

<script>
import axios from 'axios';
import marked from 'marked';

export default {
  name: 'app',
  data () {
    return {
      detail: 'これは文章ですよ。'
    }
  },
  methods: {
    async getDetail () {
      // 取得するファイルのパス
      const url = '/static/sample.md';
      try {  // 成功したら結果を渡す
        return (await axios.get(url)).data;
      } catch (e) {  // 失敗したらエラーコードを渡す
        throw e.response.status;
      }
    }
  },
  mounted () {
    this.getDetail()
      .then(result => this.detail = marked(result))
      .catch(result => alert(result));
  }
}
</script>

<style>
</style>

先程の detail に result をそのままブチ込む処理に marked() を追加した感じになりました。

結果は、ちゃんと html で吐き出されました!

最後に、シンタックスハイライトを付けちゃいましょう。やっと highlight.js の出番ですね。

とりあえずインストール。

# npm の場合
npm install highlight.js --save-dev

# yarn の場合
yarn add highlight.js

処理なのですが、 marked に、プログラミングコードがあったら highlight.js を使って変換しろよ! という設定を付与してあげます。 getDetail() が呼び出される手前に書きます。このときに、highlight.js を import するのを忘れずに……

<template>
<div id="app">
  <div v-html="detail"></div>
</div>
</template>

<script>
import axios from 'axios';
import marked from 'marked';
import hljs from 'highlight.js';
import 'highlight.js/styles/github.css';

export default {
  name: 'app',
  data () {
    return {
      detail: 'これは文章ですよ。'
    }
  },
  methods: {
    async getDetail () {
      // 取得するファイルのパス
      const url = '/static/sample.md';
      try {  // 成功したら結果を渡す
        return (await axios.get(url)).data;
      } catch (e) {  // 失敗したらエラーコードを渡す
        throw e.response.status;
      }
    }
  },
  mounted () {
    marked.setOptions({  // marked の設定
      highlight: function(code, lang) {
        return hljs.highlight(lang, code).value;
      }
    });

    this.getDetail()
      .then(result => this.detail = marked(result))
      .catch(result => alert(result));
  }
}
</script>

<style>
</style>

これでちゃんとシンタックスハイライトが付きました!

import 'highlight.js/styles/~~~ の部分でシンタックスハイライトのカラーバリエーションを指定しています。この ~~~ を書き換えることで、他のバリエーションを選択することができます。

このページを参考にしてみると良いでしょう。

長くなってしまいましたが、 Vue.js におけるマークダウンを使ったラクに文章を書いて描画するという方法でした。

axios (というか async と await)のキレイな扱い方が未だに分かっていないので、クールな組み方をご存じの方はご教授いただけると幸いです。

それでは、今日はこのへんで。

この記事が気に入ったらサポートをしてみませんか?