試行錯誤の段階を記載しているので急いでいる方は下へ。
問題
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へ動こうとしているので、何かがおかしい。
ソース
問題のソースはこれ。(抜粋)(変更)
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
)
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
の定義を見にいく。
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で確認する。
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行目、オブジェクトを参照でコピーしていることに気づいた。
let query = this.$route.query;
これを次のように変更することで修正できた。
let query = Object.assign({}, this.$route.query);
まとめ
JSのオブジェクトの代入には細心の注意を。参照渡しで痛い目にあいます。
(問題の核心はVue-routerとは少し離れますが、関係はしていたのでタグ付けしました)