2019-11-14

mdのアンカでもVueルータを利用する

Blog Nuxt化
Nuxt.js
Markdown

Markdownで書いた記事をNuxtで表示させるところまではできました。
問題になるのがリンクを表すアンカが、vue routerの仕組みに乗らないためにSPA動作にならないことです。

[記事へのリンク]()  
[外部リンク](https://www.google.com/)  

<nuxt-link to="">記事へのリンク</nuxt-link>  
<a href="https://www.google.com" target="_blank">外部リンク</a>  

になって欲しいところ。

実際には下記のようなアンカが生成されます。

<a href="">記事へのリンク</a>  
<a href="https://www.google.com/">外部リンク</a>  

内部リンクはnuxt-linkじゃないし、外部リンクも同一ウィンドウに開く仕様。

んー、、、
ちょっと無理やりですがmountedサイクルでタグの動作を変更するミックスインを作成します。

export default {  
  data: () => ({ atags: [] }),  
  mounted() {  
    const tags = this.$el.querySelectorAll('.markdown-body a[href]:not([href="#"])') // 1-i  

    for (const tag of tags) {  
      tag.setAttribute('data-x-href', tag.href) // 1-ii  
      tag.href = '#' // 1-iii  
      tag.addEventListener('click', this._open) // 1-iv  
      this.atags.push(tag) // i-v  
    }  
  },  
  methods: {  
    _open(e) {  
      e.preventDefault() // 2-i  
      const href = e.target.getAttribute('data-x-href')  
      if (href.startsWith(location.origin)) {  
        this.$router.push(href.replace(location.origin, '')) // 2-ii  
      } else {  
        window.open(href, '_blank') // 2-iii  
      }  
    }  
  },  
  beforeDestroy() {  
    // 3-i  
    for (const tag of this.atags)  
      tag.removeEventListener('click', this._open)  
  }  
}  

これを Markdown を読み込むvueファイルのミックスインに登録すれば OK です!

ソースの解説

  1. mount 時の処理

    1. ページ内のアンカタグを探す。hrefを持つけどhref="#"ではないこと
    2. href属性をdata-x-hrefに移動する
    3. href属性は一律#に置き換える。hrefを消してしまうとアンカのスタイルが効かないため
    4. クリックイベントハンドラに_open関数を登録する
    5. 変換したタグは後処理用にキャッシュしておく
  2. クリック時の処理

    1. イベントはpreventDefaultして履歴に#が残るのを防ぐ
    2. 遷移先にlocation.originが含まれていれば内部リンクなので、vueルータにプッシュする
    3. 外部リンクなら別ウィンドウで開く
  3. 後処理

    1. キャッシュしていたタグからイベントをはがす

要するにアンカのデフォルト動作を剥奪して、クリックイベントでリンク先を判断してvueルーターを使うか新規ウィンドウで開くかを決定しています。
やりたいことはできたのでOKとする!


猫派 / 基本インドア / ガジェット大好き / RDP推進派
コロナ禍の趣味はPC+VRでゲーム。
最近のゲーム:Factorio / BeatSaber / にゃんこ大戦争

→ Policy