Vueitfyのv-list-itemでネストしたpathの子のページに行くと親もactiveになってしまう問題

# あらすじ

1. `/items`と`/items/edit`のようなリンクのあるサイドバーを実現したい
2. デフォルトでは`/items/edit`を開くと`/items`もアクティブになるので困る
3. `vue-router`の`:exact`のpropをBooleanで渡して完全一致か否かを指定する
4. `exact`では`/items?category=プロテイン`の様にQuery Paramを指定するとアクティブにならない
5. 特定ページにいる場合、`:to`propに渡すpathにもquery paramを渡してあげる
6. 解決！！

## 解決コード

```vue
<template>
  <v-app>
    <v-navigation-drawer v-model="drawer" app>
      <v-list dense nav>
        <template v-for="(page, index) in pageList">
          <!-- ネストしていない構造 -->
          <v-list-item
            v-if="typeof page.items !== 'object'"
            :key="index"
            :to="page.to"
            :exact="page.exact"
          >
            <v-list-item-action>
              <v-icon>{{ page.icon }}</v-icon>
            </v-list-item-action>
            <v-list-item-content>
              <v-list-item-title v-text="page.title" />
            </v-list-item-content>
          </v-list-item>

          <!-- ネストしている構造 -->
          <v-list-group
            v-else
            :key="index"
            :prepend-icon="page.icon"
          >
            <template v-slot:activator>
              <v-list-item-content>
                <v-list-item-title v-text="page.title" />
              </v-list-item-content>
            </template>
            <v-list-item
              v-for="(childPage, cIndex) in page.items"
              :key="cIndex"
              :to="childPage.to"
              :exact="childPage.exact"
            >
              <v-list-item-icon>
                <v-icon>{{ childPage.icon }}</v-icon>
              </v-list-item-icon>
              <v-list-item-content>
                <v-list-item-title v-text="childPage.title" />
              </v-list-item-content>
            </v-list-item>
          </v-list-group>
        </template>
      </v-list>
    </v-navigation-drawer>
  </v-app>
</template>

<script>
export default {
  computed: {
    pageList () {
      const route = this.$route
      // NOTE: どうしてもクエリーパラメーターを使いたい場合は当該pathにいる場合にrouteからfullPathを取得して当てる
      const itemsPath = route.path === '/items' ? route.fullPath : '/items'
      return [
         // ホームはexactをtrueにしなくても問題無い
        {
           icon: 'home',
           title: 'ホーム',
           to: '/',
           exact: true,
        },
        {
           icon: 'shopping_cart',
           title: '商品',
           // `/items/`以下に反応されたら困るのでexactをfalse
           // NOTE: しかし、このままだとクエリーパラメーターを使えない
           items: [
             {
               title: '商品一覧',
               // to: '/items'
               to: itemsPath,
               exact: true,
             },
             // 新規と編集の両方を許可したいのでexacrtはfalse
             {
               title: '商品登録',
               to: '/items/edit',
               exact: false,
             },
           ]
        },
      ]
    }
  }
}
</script>
```

# 解説

## デフォルトでは/items/editを開くと/itemsもアクティブになるので困る

vue-touterはデフォルトでは部分一致でアクティブの判定をしてしまいます。
`/items`と`/items/edit`のような構造のpathがある場合は`exact`で完全一致でアクティブ判定をするように指定してあげます。

https://router.vuejs.org/ja/api/#exact

この時、exactはBooleanで指定できるので
完全一致にしたいものだけexactをtrueにしていしてあげる事もできます。

```vue
  <v-list-item
     v-if="typeof page.items !== 'object'"
     :key="index"
     :to="page.to"
     :exact="page.exact"
  >
```

## exact（完全一致）ではQuery Stringを指定するとアクティブにならない
exactでは/items?category=プロテインのようにQuery Stringを指定するとアクティブにならない

GitHubのissuesなどで色々漁った結果、公式で対応する予定は無さそうな雰囲気がしたので無理やり対応してみます。

https://github.com/vuejs/vue-router/issues/2040

### 特定ページにいる場合、クエリーも渡す

参考
[How do I style an active route that has query params? - by stackoverflow](https://stackoverflow.com/questions/49792077/how-do-i-style-an-active-route-that-has-query-params)

ちょっと強引な解決方法になりますが、もし他にいい方法が有ればコメントで教えて下さい！！

```vue
<script>
export default {
  computed: {
    pageList () {
      const route = this.$route
      // NOTE: どうしてもクエリーパラメーターを使いたい場合は当該pathにいる場合にrouteからfullPathを取得して当てる
      const itemsPath = route.path === '/items' ? route.fullPath : '/items'

      return [{
         icon: 'shopping_cart',
         title: '商品一覧',
         to: itemsPath,
         exact: true,
      }]
    }
  }
}
</script>
```