To-Do Calendar - Day9 Vue Router & Vuex 問題解決紀錄
寫一下在實作前端登入驗證的過程中所遇到的問題及解決方法。
問題1:重新整理頁面後,state 資料狀態丟失
store 中的資料是儲存在執行記憶體中的,所以當頁面重新整理時,store 中的資料就會重新變為初始狀態。導致雖然已經登入了,但一重新整理頁面,Vuex 狀態被清除而網頁又回到登出狀態。
一刷新頁面,又出現請重新登入
解決方法
在登入成功後,將使用者登入資訊也保存一份在 sessionStorage,當頁面重整時再透過 sessionStorage 重新賦值給 Vuex
- src/store/index.js
- 後來決定拿掉 isLogin,改是否拿到 token 來判斷登入狀態
- sessionStorage 資料的寫入和移除統一寫在 Vuex
state: { isLoginModalOpen: false, userId: sessionStorage.getItem("userId") || "", //设置初始化的值(若在 sessionStorage 中不存在則為空字串) userName: sessionStorage.getItem("userName") || "", token: sessionStorage.getItem("token") || "", }, mutations: { isLoginModalOpen(state, status) { state.isLoginModalOpen = status; }, setUserId(state, userId) { state.userId = userId; }, setUserName(state, userName) { state.userName = userName; }, setToken(state, token) { state.token = token; }, setStoreToSession(state) { sessionStorage.setItem("userId", state.userId); sessionStorage.setItem("userName", state.userName); sessionStorage.setItem("token", state.token); }, removeSession(state) { sessionStorage.getItem("userId", state.userId); sessionStorage.getItem("userName", state.userName); sessionStorage.getItem("token", state.token); }, },
- src/pages/index.vue
sendUserLogin() { let self = this; let result = true; if (self.checkLoginInput()) { //send login API to server if (result) { self.$store.commit("isLoginModalOpen", false); self.$store.commit("setUserId", "user01"); self.$store.commit("setUserName", "test"); self.$store.commit("setToken", "123"); self.$store.commit("setStoreToSession"); //提交將使用者登入資訊寫入 sessionStorage setTimeout(() => { self.$router.push("innerPage/todoLists"); }, 1000); } else { self.serverErrorMsg = "帳號或密碼錯誤"; } } },
- src/router/index.js
router.beforeEach((to, from, next) => { if (to.meta.requireAuth) { if (sessionStorage.getItem("token")) { //登入狀態改從 sessionStorage 讀取 token 判斷 next(); } else { alert("請重新登入"); store.commit("isLoginModalOpen", true); next("/"); } } else { next(); } });
運行結果
問題2:在瀏覽器輸入內頁網址,導回首頁後沒有開啟登入 Modal
在未登入狀態,從首頁輸入內頁網址,能正常運行攔導回首頁並自動開啟登入 Modal,但在瀏覽器輸入內頁網址,被導回首頁後卻沒有自動開啟登入 Modal,點 Start 按鈕也無法開啟登入 Modal。
從首頁輸入內頁網址(自動開啟登入 Modal:成功) 在瀏覽器輸入內頁網址(自動開啟登入 Modal:失敗)
我在 index.vue 的 created hook、mounted hook 和 router 都加了 log,來比較一下兩邊的 console 內容:
從首頁輸入內頁網址

一進首頁先執行 created function 和 mounted function,直到輸入內頁網址,被導航守衛攔截後提交 isLoginModalOpen 為 true,觸發 watch 開啟登入 Modal。
在瀏覽器輸入內頁網址

在進到內頁網址之前,先被導航守衛攔截後提交 isLoginModalOpen 為 true,導回首頁才執行 created function 和 mounted function。
而 computed 屬性是在 created 階段建立,DOM 節點、事件都綁定至 Vue 的實體是在 mounted 階段。因為此次操作的 Vue 實體是在導航守衛導回首頁之後才建立,所以無法監聽到在建立之前的 isLoginModalOpen 狀態改變,來打開登入 Modal(就算監聽到應該也打不開,因為 Vue 實體與掛載還沒完成)。
而按 Start 按鈕也無法開啟登入 Modal,是因為 isLoginModalOpen 在被導航守衛提交為 true 後,再點 Start 按鈕經過導航守衛攔截後還是提交 isLoginModalOpen 為 true,watch 沒有監聽到 isLoginModalOpen 狀態改變,所以並不會開啟登入 Modal。
Vue 的生命週期與 Hooks function
解決方法
在 mounted function 補上一個判斷,如果 isLoginModalOpen 為 true,就開啟登入 Modal,代表這是直接在瀏覽器輸入內頁網址過來的,因為可以在 Vue 實體建立前就提交 isLoginModalOpen 為 true 的就只有 router 了,補上之後後續登入 Modal 又能正常運作。
