[VUE.JS]Vue Router使用時にページ内アンカーにリンクする方法

Vue.jsを使用しているプロジェクトで、ページ内のアンカーポイントにリンクするボタンの実装をしました。
単純に、

<a href="#xxxx">テストリンク</a>

でいいのかと思ったら、上記で実装した場合、アンカーに飛ぶには飛ぶのですが、ページのURLが/#xxxxになってしまいました。
リロードするまでは、正常に動作していたんですが、リロードしたら真っ白に!
Vue Routerの設定によっては、そもそもアンカーが動作しなかったりする場合もあります。
ということで、下記のように解決しました。

scrollIntoViewでアンカーポイントまでスクロールする

まずは、同ページ内のアンカーに遷移してみます。
javascriptを使用して移動させる方法です。
単純ですが、動作にブレが無くいい感じです。

// button part
<a class="btn-primary" @click="scrollToAnchorPoint('testBlock')">
    Scroll Test
</a>

// anchor point
<div ref="testBlock">
    In page anchor point
</div>

//  methods
<script>
export default{
    ...
    methods: {
        scrollToAnchorPoint(refName) {
            const el = this.$refs[refName]
            el.scrollIntoView({ behavior: 'smooth'})
        }
    }
}
</script>

Vue.jsでDOM要素にアクセスする場合、refで名前を付けます。
this.$refsで要素にアクセスが可能です。
behaviorでスクロールの仕方を定義しています。

他のページからアンカーポイントにリンクする

今度は外部ページからアンカーポイントに遷移させたい場合の方法です。
URLのハッシュを読み込んで移動するように実装します。

routerlinkにhashを付ける方法

下記のように記述します。

//not bind
<router-link to="/testpage#testBlock">
    Link to TestBlock in TestPage
</router-link>

//use bind
<router-link
    :to="{
        name: 'testPage',
        hash: '#testBlock'
    }"
>
    Link to TestBlock in TestPage
</router-link>

hashからアンカーポイントまでスクロール

URLで受け取ったハッシュから該当のポイントまでスクロールさせます。

export default{
    data: function(){
        return {
            hash: this.$route.hash,
        }
    },
    mounted() {
        this.$nextTick(function () {
            if (this.hash) {
                const refName = this.hash.replace('#', '')
                this.scrollToAnchorPoint(refName)
            }
        })
    },
    methods: {
        scrollToAnchorPoint(refName) {
            const el = this.$refs[refName]
            el.scrollIntoView({ behavior: 'smooth' })
        }
    }
}

this.$route.hashでハッシュを取得しています。
DOMが生成された後にscrollToAnchorPointを実行したいので、
this.$nextTick()を使用して読み込み完了時に実行しています。

画面で画像(image等)を多用している場合

遷移した先で画像などを多用している場合は、DOM生成時に位置ずれが発生し正確な場所にスクロールしません。
これはDOMの高さを指定してあげることで解決できます。
例)

<div style="height: 400px;">
    <img src="/image/test.png">
</div>

注意としては画像自体の幅高さではなく、外側の要素に対して高さを指定することです。
とはいえ、全部に高さを指定するのが不可能な場合もあると思います。
その場合は、関数の実行を遅らせることでも対応できます。

mounted() {
    this.$nextTick(function () {
        if (this.hash) {
            const refName = this.hash.replace('#', '')
            setTimeout(() => {
                this.scrollToAnchorPoint(refName)
            }, 100)
        }
    })
},

setTimeoutで実行を遅らせます。
遅らせる時間はサイトの量で調整してください。

5分で終わると思ってたアンカーポイントの実装に、以外と苦労したので記事にしてみました。