vue-scrollto などの smooth scroll を利用する対象に、 nuxt-link を指定すると正しくスクロールしない

nuxt 利用時、URLに hash ( #hoge ) を設定すると同時に、スムーズにするロールするを行いたい場合、一般の方が書いた記事には、to をカラにすればいいと書いてありますが、正しく動作しない条件が存在します。

<div id="note"></div>
<nuxt-link to v-scroll-to="'#note'">noteクリック</nuxt-link>
<div id="memo"></div>
<nuxt-link to v-scroll-to="'#memo'">memoクリック</nuxt-link>

このように記載した上で、URLにハッシュを設定しブラウザをリロード。この状態で、nuxt-link をクリックすると、window.pageYOffset が 0 が入り、スクロール開始位置が間違っているため、とくにスクロール方向が逆になった時に違和感が大きくなってしまいます。フッターに # がついたリンクがあると特に発生しやすいはずです。

vue-scrollto や smooth-scroll が悪いわけではなく、
初回表示時にnuxt-linkをクリックすると、nuxt-link がまず pageYOffset = 0 にスクロール設定することが原因でした。
その後スクロールが開始するため、正し位置からスクロールしません。

対処するためには、以下のように、nuxt-link 代わりの component を作るのがよいのではないでしょうか。

<template lang="pug">
a(:href="to" @click.stop="onClick")
  slot
</template>

<script lang="typescript">
import {
  defineComponent,
  getCurrentInstance
} from '@nuxtjs/composition-api'
import TWEEN from '@tweenjs/tween.js'

type Props = {
  path: string,
  hash: string | null,
  anker: string,
}

export default defineComponent({
  props: {
    path: {
      required: true,
      type: String
    },
    hash: {
      type: String
    },
    anker: {
      required: true,
      type: String
    }
  },
  setup ({ path, hash, anker }: Props, { emit }) {
    const current = getCurrentInstance()

    const scrollTo = (anker: string) => {
      const dom = document.querySelector(anker)
      if (!dom) { return }
      const end = dom.getBoundingClientRect().top + window.pageYOffset

      // tween.js の例
      const data = { y: window.pageYOffset }
      new TWEEN.Tween(data)
        .to({ y: end }, 500)
        .easing(TWEEN.Easing.Exponential.InOut)
        .onUpdate(() => { window.scrollTo({ top: data.y }) })
        .start()

      const animate = (time: number) => {
        if (TWEEN.update(time)) {
          requestAnimationFrame(animate)
        }
      }
      animate(0)
    }
    const to = hash === undefined ? `${path}${anker}` : `${path}${hash}`
    const onClick = () => {
      if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) {
        // do nothing
      } else
      if (current?.$route.path === path) {
        e.preventDefault()
        window.history.replaceState(null, '', to)
        scrollTo(anker)
      } else {
        e.preventDefault()
        current?.$router.push(to)
      }
    }
    return {
      to,
      onClick
    }
  }
})
</script>