こんにちは新人エンジニアの三上と申します。
Vue.js を使用した実装の際に、思いがけず詰まってしまったことを備忘として残しておきたいと思います。
window.location.reload() について
window.location.reload() を呼び出すことで、現在のページのURL を再読み込みすることが出来ます。
これに合わせて、ページ読み込みの際の処理を追加することで、ファイルを分けることなくページ内要素を更新することが出来ます。
では、実際にwindow.location.reload()を利用した実装を以下に例示しようと思います。
<template>
<p>{{ store.tmpCount }}</p>
<button @on-click="onPush">
<p>押下</p>
</button>
</template>
<script>
import { returnStore } from '@/stores'
// store 定義(※)
const store = computed(() => {
return returnStore()
})
// ボタン押下時、呼び出し
function onPush(){
window.location.reload()
}
// ページ読み込みの際に呼び出し
onBeforeMount(() => {
store.tmpCount += 1
})
</script>
※store については以下参照
ページ内の「押下」ボタンを押すことで、window.location.reload()によりページを再読み込みし、store に保持している「store.tmpCount」を 1 加算し、表示されている数値が変化するという実装です。
実際に問題が発生した点
実際に window.location.reload() を使用し、つまずいた実装を以下に示します。
<template>
<p>{{ store.tmpCount }}</p>
<Button @click="funcTmp">
<p>押下</p>
</Button>
</template>
<script>
import { returnStore } from '@/stores'
// store 定義
const store = computed(() => {
return returnStore()
})
const funcTmp = () => {
const tmp = null
console.log('処理A')
if (0 == 0) {
//API 呼び出し 数値を返却する
store.apiCall(
{
parameter: '引数',
},
(res) => {
console.log('関数呼び出しB: ' + res.result)
tmp = res.result //APIのレスポンスを格納
},
(err) => {
console.log(error)
}
)
} else {
store.apiCall2(
{
parameter: '引数',
},
(res) => {
console.log('関数呼び出しB: ' + res.result)
tmp = res.result //APIのレスポンスを格納
},
(err) => {
console.log(error)
}
)
}
console.log('処理C: ' + tmp) //APIのレスポンスをコンソールに表示
store.tmpCount = tmp // APIレスポンスをstoreに保存
window.location.reload() //ページ再読み込み
return
}
onBeforeMount(() => {
if(store.tmpCount >= 10) {
store.tmpCount = 0
}
})
</script>
上記実装において、想定される処理順は以下の通りです。
① 「押下」ボタンを押したときに、funcTmp()関数が呼び出される。
② funcTmp()関数として、APIを呼び出し、返却値を「tmp」に保存する
③ 「tmp」の値を「store.tmpCount」に保存する
④ window.location.reload() によりページを再読み込みすることでonBeforeMount()が呼び出される。
⑤ APIの返却値である「store.tmpCount」を判定し、返却値が10以上であれば0を代入する。
しかし、実際にdevツールを使い、処理順を解析したところ以下の順序で処理が行われていることが分かりました。
① 「押下」ボタンを押したときに、funcTmp()関数が呼び出される。
③ 「tmp」の値を「store.tmpCount」に保存する
④ window.location.reload() によりページを再読み込みすることでonBeforeMount()が呼び出される。
⑤ APIの返却値である「store.tmpCount」を判定し、返却値が10以上であれば0を代入する。
② にあたる処理が行われていないことが分かりました。
原因
想定される原因としては、APIの呼び出しが非同期処理となっており、それ以外の処理が並行処理されることで、APIのレスポンスが返却される前に、ページの更新処理である window.location.reload() が行われていると考えられます。
※同期処理・非同期処理については以下参照
実装修正
上記原因から修正方針を立て、funcTmp()関数の呼び出し、及びAPI呼び出しを同期処理とすることで想定の順序通りに処理が行われました。
以下に、修正した実装を示します。
<template>
<p>{{ store.tmpCount }}</p>
<Button @click="funcTmp">
<p>押下</p>
</Button>
</template>
<script>
import { returnStore } from '@/stores'
// store 定義
const store = computed(() => {
return returnStore()
})
async function funcTmp () {
const tmp = null
console.log('処理A')
if (0 == 0) {
//API 呼び出し 数値を返却する
await store.apiCall(
{
parameter: '引数',
},
async (res) => {
console.log('関数呼び出しB: ' + res.result)
tmp = res.result //APIのレスポンスを格納
},
async (err) => {
console.log(error)
}
)
} else {
await store.apiCall2(
{
parameter: '引数',
},
async (res) => {
console.log('関数呼び出しB: ' + res.result)
tmp = res.result //APIのレスポンスを格納
},
async (err) => {
console.log(error)
}
)
}
console.log('処理C: ' + tmp) //APIのレスポンスをコンソールに表示
store.tmpCount = tmp // APIレスポンスをstoreに保存
window.location.reload() //ページ再読み込み
return
}
onBeforeMount(() => {
if(store.tmpCount >= 10) {
store.tmpCount = 0
}
})
</script>
まとめ
window.location.reload()を使用したことで、想定とは異なる挙動をしたと考えられます。
devツールを使用してデバッグポイントを貼り付け、処理順が想定通りになっているか確認することで、問題を発見することが出来ました。
関数内でのAPI呼び出し処理を非同期にすることで、レスポンスを取得後にページの更新処理を行うことができました。
以上、三上でした。