Nuxt/Vueについての個人的知見まとめ

## はじめに
Nuxtを使った開発が続いたので、その知見(のようなもの)を共有したいと思います。
わざわざ単ページにするほど長くもないトピックばかりです。

## aタグではなくnuxt-linkを使う

`nuxt-link`というカスタムタグ(コンポーネント)はnuxt特有のもので、Vue-routerの`router-link`のラッパーです。画面遷移に先んじて遷移先のページを読み込んでいるので、特別な事情がない限り基本はnuxt-linkを使いましょう。

気をつけてほしいのは、どこからかコピペしてきたhtmlタグにうっかりaタグが混じっているパターンです。たいていの場合はaタグでも問題にならずに動きます。一応普通に画面遷移自体はできるのですが、明らかにその画面遷移が遅いのと、Vuexの状態管理情報が消えるので、認証状態でaを使うと認証されず気づかずにエラーがでるということもあります。
aタグによる画面遷移は遅いので慣れてくるとaタグが混じってることに感覚的に気づくと思います。

### window.locationの代わりに$router.pushを使う

**※以下の内容はNuxtのバージョン、APIによってrouter.push()の使い方が代わるかもしれません**

少なくともNuxt v2.14.0+Composition APIの場合ではですが、$router.pushは以下のようにして使います。

```TypeScript
  setup(_props: {}, { root }: SetupContext) {
    root.$router.push('/test') // 飛ぶ先

    return {}
  },
```

$router.pushを使うとstoreされた情報が維持されます。逆にwindow.locationの別のページに飛ぶとstoreの情報が消えるかもしれないのでご注意を。

## ページ内リンクのジャンプ

ハッシュ記号によるページ内リンク指定ですが、普通のaタグのやり方とは少し違います。
例えば、index.vueからabout.vue内にある#titleというアンカーに飛ぶというときは以下のようにnuxt-linkを貼ります。

```vue:index.vue
<nuxt-link :to="{ path: 'about', hash: '#title' }">link</nuxt-link>
```

about.vue内では以下のようにします。

```vue:about.vue
    <div id="title">
      とべた！
    </div>
```

※自分の場合`<a name="title"></a>`は使えませんでした。
あくまでidを指定していることに注意。
なお、この方法は同じページ内だと最初の一回しかジャンプできません。
一度リンク先に飛んでから、もう一度リンクボタンをクリックしても何も反応しません。何度でもジャンプしたいときは、Vue-scrolltoを使用してください。

また、hashを使って飛ぶ方法は$router.pushでも可能なので、何らかの処理を実行したあとにページ内リンクで飛ばしたいというときは以下のような記述で飛べます。

```TypeScript
root.$router.push({ path: '/about', hash: 'title' })
```



## スムーズスクロールで何度でもページ内ジャンプをしたい

モジュールとして[vue-scrollto](https://github.com/rigor789/vue-scrollto)を使用しましょう。

```
yarn add vue-scrollto
```

nuxt.config.jsには以下のように

```JavaScript
{
    modules: [
        'vue-scrollto/nuxt',

        // もしもカスタムオプションをつけたいときは以下のようにする
        ['vue-scrollto/nuxt', { duration: 300 }],
    ]
}
```

リンクは以下のように貼ります

```vue
<nuxt-link v-scroll-to="'#here'" to>link</nuxt-link>
```

飛ぶ先にもid指定でhereを用意しておきます。

```vue
    <div id="here">
      とべた！
    </div>
```





## watchで指定した複数の要素をwatchする

これはNuxtとかCompositionAPIとか関係なしに昔から使えましたが、備忘録に。
全部の要素ではなくいくつかの複数の要素をwatchしたいというときは以下のように配列で渡します。

value1かvalue2のどちらかが変化したとき、changeと表示されます。

```TypeScript
    const value1 = ref(1)
    const value2 = ref(1)
    watch(
      () => [value1.value, value2.value],
      () => {
        alert('change!')
      }
    )
```



## ルート情報を取得する

ルート情報は`root.$route.path`で取得できます。
例えばメインメニューのようなものがペッジーヘッダーにあり、現在表示されているページにはアンダーバーを表示するクラスを追加したい、という要望があるかもしれません。仮に追加したいクラス名を`current-menu-item`としたときは、以下のようにします。

```TypeScript
	// メニュー    
    const menuList = [
      { name: 'index', path: '/' },
      { name: 'about', path: '/about' },
      { name: 'contact', path: '/contact' },
    ]
    const menuClass = (path: string) => {
      return root.$route.path === path ? 'current-menu-item' : ''
    }
```

テンプレート

```vue
    <ul>
      <li
        v-for="menu in menuList"
        :key="menu.name"
        :class="menuClass(menu.path)"
      >
        <nuxt-link :to="menu.path">{{ menu.name }}</nuxt-link>
      </li>
    </ul>
```

上記は文字列として一つのクラスを渡していますが、配列で複数のクラスを渡せるようにしたほうが応用が効きます。



## タイトルを動的に変更する

Nuxtでは内部的に [vue-meta](https://vue-meta.nuxtjs.org/)を使用しています。
nuxt.config.jsを開き、`head`プロパティのtitleを消し、代わりにtitleTemplateを使います。プレースホルダーとして%sを書きます。

```JavaScript
  head: {
    // title: 'サイト名',
    titleTemplate: 'サイト名 | %s',
  }
```

あとは各ページコンポーネント上で、titleプロパティをいじればOK。

```JavaScript
export default {
  name: '',
  head: {
    title: 'ここに追加タイトル',
  },
  setup(){}
}
```

titleタグに`サイト名 | ここに追加タイトル`と入っていれば成功です。

参考: [Nuxt.js titleタグを各ページごとに「サイト名 + 任意の文字列」に設定](https://mebee.info/2020/01/01/post-4573/)

## ビルド時にファイルを生成したいとき
ビルド時に特定のファイルを仕込むときに使えます。

参考 : [Nuxt.jsのhooksを利用してビルド時にファイルを生成する](https://polidog.jp/2019/03/08/nuxt_hooks/)

## ページのトップに飛ぶリンクを作る

```
yarn add vue-backtotop
```

インポートについてVueは[公式サイト](https://caiofsouza.github.io/vue-backtotop/)から。以下はNuxtの場合のやり方です。

plugins/vue-go-top.jsを作ります。

```JavaScript
import Vue from 'vue';
import GoTop from 'vue-backtotop';

Vue.use(BackToTop)
// もし`go-top`というカスタムタグを使いたいときは以下のようにする
// Vue.component('go-top', GoTop)
```

nuxt.config.jsのpluginsプロパティ配列に以下を追記

```JavaScript
plugins: [
    {
      src: '@/plugins/vue-go-top',
      mode: 'client'
    }
  ],
```

テンプレート部

```vuejs

<BackToTop text="Topへ" />
<!-- 縦に長いページを作ると右下に自動的に表示されます -->
```

ただしデフォルトのデザインはそんなに格好良くないので、自分で書き換えたほうがいいです。
書き換える場合は以下のようにします。自分のデザインに書き換えたいときは適当にbuttonタグ部分を変更してください。

```vuejs
    <BackToTop>
      <button>トップへ</button>
    </BackToTop>
```


参考 : [Nuxt.js vue-go-topを使用して画像にエフェクトかける](https://mebee.info/2020/02/10/post-5991/)



## コンポーネントを使うときはcomponentsモジュールを使う

Nuxtでコンポーネントを扱うときは、`components`モジュールを使うことを強くお勧めします。
これは比較的最近のバージョンで標準化されたモジュールなので、最新版ではインストールした段階で使用できます。
古いバージョンだとモジュールをインストールすれば使用できるので、[こちら](https://qiita.com/JDM/items/afb5171594f69bcd94bd)を御覧ください。

今までのNuxtだと、コンポーネントを使用する際、まず使用したいコンポーネントをimportしてcomponentプロパティに使用するコンポーネントを指定してからテンプレート上で使えるようにしていました。
しかしcomponentsモジュールがあると、ただ`<HelloWorld />`とテンプレート上で書くだけで同名のvueファイルが読み込まれます。NuxtのComponentモジュールがあれば、自動的にそのモジュールが呼ばれるので、モジュールだけ適当に切り分けて、後でディレクトリごとにまとめるという開発のススメ方ができますので、これが割と便利です。
なお同名のコンポーネントが存在するとエラーが出るので、こっそり違うコンポーネントが表示されることはありません。

もしかしたら古くからNuxtを使っている方も知らないかもしれないので、これはオススメです。