iT邦幫忙

2025 iThome 鐵人賽

DAY 28
0
Software Development

全端工程師團隊的養成計畫系列 第 28

Day28 前後端分離的魔鬼與細節(二)

  • 分享至 

  • xImage
  •  

在傳統的 form post 模式下,使用者送出表單時會執行 form submit,瀏覽器會將整個頁面資料一次送往伺服器端。伺服器接收後處理商業邏輯,再重新產生 HTML,並回傳給瀏覽器進行頁面重整。換句話說,每次新頁面顯示的內容,都是伺服器重新組裝後的最新資料。

然而在 Single Page Application (SPA) 架構下,應用的狀態會被保留在記憶體中(例如 Pinia 或元件的狀態)。「資料一致性」要自己管控,如果不主動重新抓取或更新,資料就會停留在舊的狀態。也就是說,每當操作完成後若有資料需要更新,必須在前端額外加入「同步資料」的邏輯。以下將透過兩個案例來說明:

明細資料異動後,主表資料內容需要自行更新

回到 Day24 所介紹的「功能授權」實作中,若要異動群組名稱(如圖28-1,標示 1),並在點選「儲存」後,除了需要發送 API 請求來更新群組名稱並確認成功外,還需要再執行一次 同步查詢群組名稱資訊的 API 請求(如圖28-1,標示2),以確保主表資訊保持最新。
在本案中,我們選擇將這個同步作業放在「關閉視窗」時執行,藉此簡化 SaveCancel 各自負責的工作範圍。否則,測試者很可能會因資料不同步而回報 Bug,最後就會在你的待辦清單中多一張任務 Ticket。
圖28-1:當完成編輯並儲存資料異動後,仍須同步更新前端視窗的顯示資料,確保畫面一致性。
圖28-1

當然,還有另一種作法:將查詢頁的資料與編輯視窗的資料綁定到同一個 store(如圖28-2),即可自動達到同步效果,省去額外的同步邏輯。不過這仍需視設計結構而定。有些情況下,若考量效能或 Dto 複雜度,可能不適合將所有明細資料完整展開並綁定在同一個 store 變數中。這時就需要在設計上權衡,若資料量過大或 Dto 結構過於複雜,則需要特別注意效能與維護成本,決定是否採用這種自動化同步方式。
圖28-2:兩個不同 Page 之間的資料同步,也可以透過綁定相同的 store 來達成,不過是否適用仍需依資料結構而定。
圖28-2

我們展開檢視功能授權的查詢頁面後,可以發現資料結構並非單一明細,而是同時包含「人員帳號的授權」與「功能清單的設定」。這些都需要透過此查詢介面作為異動的入口。

因此,並不適合採用將資料綁定到同一個 store 的方式來控管一致性。若強行綁定在一起,不僅程式耦合度提高,光是 Dto 結構的設計與維護就會變得更加複雜。
圖28-3:這類情境下,建議透過 API 同步 的方式來確保資料正確性與維護性。
圖28-3

Data table 的選取列,選取後要自行清空

在 Vue 3 中,v-model 並沒有自動清空的機制,而 easy-data-table 也沒有內建「當資料變動時自動清空選取」的選項,因此需要自行處理。

延續 Day27 提到的功能,我們希望能在查詢介面中完成多群組的停用(刪除)操作。
因此在 easy-data-table 中,額外新增了一個雙向綁定的 v-model 變數:selectedGroupList,用來保存當前被選取的群組,如圖28-4 所示。
圖28-4
圖28-4:群組的選取結果將綁定至變數 selectedGroupList

我們嘗試先刪除「UserGroupIT(ID=122)」,刪除成功後再刪除「UserGroupHR(ID=121)」,結果後端卻回應:「查無資料」。但實際查詢(圖28-5)可以看到資料仍然存在。

於是,恭喜再次收穫一張 Bug 單。當然,我們鼓勵在測試環境中多犯錯來提早發現問題,不過如果這種情況一直發生,還是會讓人感到相當心煩,尤其是回想過去使用傳統 form submit 時,根本不需要額外處理這些細節。

圖28-5:刪除「UserGroupIT」後,再刪除「UserGroupHR」時,系統回應「查無資料」。
圖28-5

讓我們來看看原因,實際查詢後確認,「UserGroupHR(ID=121)」這筆資料仍完整存在於資料庫中。(圖28-6)。
圖28-6:UserGroupHR(ID=121)實際是有資料的。
圖28-6

來看看前端的 Console,變數 selectedGroupList 實際上同時送出了兩個群組的刪除請求,其中包含先前已被刪除的群組。
圖28-7:透過 Console 檢查實際被選取的資料內容。
https://ithelp.ithome.com.tw/upload/images/20251004/20178758jpnaNsarwj.png

提供以下方式可以排除上述的問題


const deleteGroups = async () => {

  if (selectedGroupList.value.length === 0) {
    OpenDialogAlert("請選擇要刪除的群組");
    return false;
  }
   //向後端送出刪除群組的 API 請求,並且帶入勾選的 item 項目
  if(await store.deleteGroup( selectedGroupList)) {  
    selectedGroupList.value = []; // 執行成功,清除已經選取的值
    QuerygroupList(); // 同步群組資料
   
  } 
  OpenDialogAlert(store.rsp.message)//顯示執行結果

};

Ending Remark

有別於 form submit 模式下,伺服器每次都會回傳完整頁面,因此「資料一致性」由後端自動完成;但在 SPA 架構 中,狀態留在記憶體(Pinia / 元件),若不主動處理,畫面就會殘留舊資料,必須靠前端自行加入同步邏輯。

今日分享了兩個典型案例:

明細更新後需同步主表:修改群組名稱時,除了呼叫更新 API,還需額外查詢一次主表,否則畫面資料不同步。若綁定同一個 store 雖可達到自動同步,但在資料結構複雜時會增加耦合與維護成本,建議改採 API 同步。

Data table 選取列清空問題:Vue 3 的 v-model 與 easy-data-table 並不會自動清空選取,若刪除多筆資料後未清空選取,可能導致「已刪除資料再送一次」的錯誤,進而產生 Bug。解法是在刪除成功後手動清空 selectedGroupList 並重新查詢資料。

在 SPA 架構下,資料一致性要自己管,不同步就等著收 Bug 單。


上一篇
Day27 前後端分離的魔鬼與細節(一)
下一篇
Day29 從傳統post 到現在前後端分離,前端思維的改變
系列文
全端工程師團隊的養成計畫30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言