在 Nuxt 中,當我們 refresh 頁面時,寫在 data 或 computed 中的程式碼,在 server 端與 client 端都會各執行一次,並且在前端會進行html tag的比對,若兩者不相符會噴錯,甚至沒有辦法進行接下來的操作。
因此,當我們用了 new Date().getTime(), Math.random() 這類的程式碼在data中,是一定會造成錯誤的。甚至 new Date().getDay() 也是有可能,因為 server time 有很大的可能性,跟 client 端的時區不同。最後我們再來看看如何解決,我們先來看看相同的程式碼,在 Nuxt2 與 Nuxt3 分別會導出怎樣的結果。
建立測試頁面
我們先分別開 Nuxt2 與 Nuxt3 兩個環境,分別給 Nuxt2 & Nuxt3 建立一個 Option API 的頁面,原始碼如下:
<template> <div> Test Render Error <hr> <div v-if="isMonday"> isMonday: {{ isMonday }} <hr> </div> <button type="button" class="btn" @click="doSomething()" > doSomething() </button> </div> </template> <script> export default { data () { const isMonday = process.server ? new Date(new Date().getTime() - 18 * 60 * 60 * 1000).getDay() === 1 : new Date().getDay() === 1 console.log(isMonday) return { isMonday } }, methods: { doSomething () { console.log('doSomething()') } } } </script>
isMonday 的部分為了在 server 模擬不同時區,給他 new Date(new Date().getTime() – 18 * 60 * 60 * 1000).getDay() === 1,而 client 端就是 new Date().getDay() === 1,這部分各位在測試時,可以依照時間做調整。
另外我們在,Nuxt3環境中,另外建立一頁 Composition API 的頁面,用於測試 Composition API 是否會得到不同的結果,原始碼如下:
<template> <div> Test Render Error <hr> <div v-if="isMonday"> isMonday: {{ isMonday }} <hr> </div> <button type="button" class="btn" @click="doSomething()" > doSomething() </button> </div> </template> <script setup> const isMonday = process.server ? new Date(new Date().getTime() - 18 * 60 * 60 * 1000).getDay() === 1 : new Date().getDay() === 1 console.log(isMonday) function doSomething () { console.log('doSomething()') } </script>
測試結果 Nuxt2 與 Nuxt3 結果大不同
在 Nuxt2 的開發環境中,若發生render錯誤,會出現類似下圖的錯誤訊息,但 method 還是可被執行
而在正式環境中,情況就比較不妙。當發生錯誤時,會出現下圖的錯誤訊息,雖然 NuxtLink 還可以正常換頁,但 call method 時卻毫無反應。假設我們的頁面主要功能都是以 methods 做反饋,一但 render 錯誤,這一頁就會死掉。也就是說,在測試環境中若沒注意到錯誤訊息、沒有解決,在正式環境中會導致使用者無法操作。
而在 Nuxt3 中,無論是用 composition API、option API ,無論是正式或是測試環境,都可以正常運作,僅僅在測試環境會噴警告而已,如下圖:
下圖是 Nuxt3 正式環境,一樣可以執行 doSomething()
下圖是 Nuxt3 測試環境,我們可以看到,即使 isMonday 與前台不同,給了 false,還是沒有造成前台會無法繼續操作的嚴重錯誤。
結論
目前看起來,這種情況的 render 錯誤,需要在 Nuxt2 上多加注意,一個不小心可能會導致使用者無法操作的這種低級錯誤,而在 Nuxt3 上目前沒有看得出來的影響。
Nuxt2的解決方式
如前述,在 Nuxt 中,當我們 refresh 頁面時,寫在 data 或 computed 中的程式碼,在 server 端與 client 端都會各執行一次。因此,我們可以用 asyncData 來設定有用到 Math.random(), new Date().getTime() 這類的資料,這樣 refresh 時就不會在 client side render 時重複跑一次。當然,在只有 client sider render 的換頁情況下也不會有問題。