mdのアンカでもVueルータを利用する
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 です!
ソースの解説
- mount 時の処理
- ページ内のアンカタグを探す。
href
を持つけどhref="#"
ではないこと href
属性をdata-x-href
に移動するhref
属性は一律#
に置き換える。href
を消してしまうとアンカのスタイルが効かないため- クリックイベントハンドラに
_open
関数を登録する - 変換したタグは後処理用にキャッシュしておく
- ページ内のアンカタグを探す。
- クリック時の処理
- イベントは
preventDefault
して履歴に#
が残るのを防ぐ - 遷移先に
location.origin
が含まれていれば内部リンクなので、vueルータ
にプッシュする - 外部リンクなら別ウィンドウで開く
- イベントは
- 後処理
- キャッシュしていたタグからイベントをはがす
要するにアンカのデフォルト動作を剥奪して、クリックイベントでリンク先を判断してvueルーター
を使うか新規ウィンドウで開くかを決定しています。
やりたいことはできたのでOKとする!