Vue Routerでquery書き換えができなかった(凡ミス)

試行錯誤の段階を記載しているので急いでいる方は下へ。
## 問題
Vue Routerを使用してqueryを書き換えようとすると次のように怒られた。

```
Uncaught (in promise)
NavigationDuplicated {
    _name: "NavigationDuplicated",
    name: "NavigationDuplicated",
    message: "Navigating to current location ("/blog?page=3") is not allowed",
    stack: "Error↵    at new NavigationDuplicated (webpack-int…node_modules/vue/dist/vue.runtime.esm.js:1853:26)"
}

Error
    at new NavigationDuplicated (webpack-internal:///./node_modules/vue-router/dist/vue-router.esm.js:2013:14)
    at HTML5History.confirmTransition (webpack-internal:///./node_modules/vue-router/dist/vue-router.esm.js:2129:18)
    at HTML5History.transitionTo (webpack-internal:///./node_modules/vue-router/dist/vue-router.esm.js:2073:8)
    at HTML5History.replace (webpack-internal:///./node_modules/vue-router/dist/vue-router.esm.js:2416:10)
    at eval (webpack-internal:///./node_modules/vue-router/dist/vue-router.esm.js:2829:22)
    at new Promise (<anonymous>)
    at VueRouter.replace (webpack-internal:///./node_modules/vue-router/dist/vue-router.esm.js:2828:12)
    at Proxy.set (webpack-internal:///./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/ts-loader/index.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/views/blog/article_list.vue?vue&type=script&lang=ts&:190:20)
    at callback (eval at ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"900baf1e-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/views/blog/article_list.vue?vue&type=template&id=ee03622c&scoped=true& (http://localhost:8080/js/article_list.js:23:1), <anonymous>:115:29)
    at invokeWithErrorHandling (webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:1853:26)"
 at invokeWithErrorHandling (webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:1853:26)"
```

どうやら同じrouteに遷移しようとしていると思われているらしい。
実際はちゃんと別routeへ動こうとしているので、何かがおかしい。

### ソース
問題のソースはこれ。(抜粋)(変更)

```javascript
let query = this.$route.query;
query["page"] = page.toString();
this.$router.push({ query });
```

## GitHubを調査
https://github.com/vuejs/vue-router/issues/2872
それっぽいissueをみつけた。
解決方法としては、Exceptionを握りつぶすというもの。
(わざと同じルートへ動こうとしたときのissueだろうか?)

ただ、同じルートへ遷移しようとすると無視するという仕様のもとでエラーを無視したところで得られるものは何もなく…

## ソースを観察
stacktraceをもとに元凶であると思われるファイルへ。(`node_modules/vue-router/dist/vue-router.esm.js:2129`)

```javascript:vue-router.esm.js
  if (
    isSameRoute(route, current) &&
    // in the case the route map has been dynamically appended to
    route.matched.length === current.matched.length
  ) {
    this.ensureURL();
    return abort(new NavigationDuplicated(route))
  }
```

`isSameRoute`の定義を見にいく。

```javascript:vue-router.esm.js
function isSameRoute (a, b) {
  if (b === START) {
    return a === b
  } else if (!b) {
    return false
  } else if (a.path && b.path) {
    return (
      a.path.replace(trailingSlashRE, '') === b.path.replace(trailingSlashRE, '') &&
      a.hash === b.hash &&
      isObjectEqual(a.query, b.query)
    )
  } else if (a.name && b.name) {
    return (
      a.name === b.name &&
      a.hash === b.hash &&
      isObjectEqual(a.query, b.query) &&
      isObjectEqual(a.params, b.params)
    )
  } else {
    return false
  }
}
```

しっかりqueryが異なることを確認しているように見える。

## 実験
先の`isSameRoute`の冒頭でa,bの中身をconsole.logで確認する。

```javascript
a: {
  name: "article_list",
  meta: {},
  path: "/blog",
  hash: "",
  query: {page: "3"},
  params: {},
  fullPath: "/blog?page=3",
  matched: [{…}],
  __proto__: Object
}

b: {
  name: "article_list",
  meta: {},
  path: "/blog",
  hash: "",
  query: {page: "3"},
  params: {},
  fullPath: "/blog?page=2",
  matched: [{…}],
  __proto__: Object
}
```

queryとfullPathで齟齬が出ていることがわかった。
原因を考えてみると、問題のソースの1行目、オブジェクトを参照でコピーしていることに気づいた。

```javascript
let query = this.$route.query;
```

これを次のように変更することで修正できた。

```javascript
let query = Object.assign({}, this.$route.query);
```

## まとめ
JSのオブジェクトの代入には細心の注意を。参照渡しで痛い目にあいます。

(問題の核心はVue-routerとは少し離れますが、関係はしていたのでタグ付けしました)