在實作了多項功能後,接下來想聊聊前後端分離所帶來的改變。
由於過去的經驗多半是以 MVC (Razor) 為主,因此這裡將以 MVC 模式 與本案的 前後端分離 (SPA) 模式 進行比較。
我們會以本案的實際需求作為範例,透過「查詢」與「編輯」這兩個需求,並以下方兩個 User Story 作為開發案例(圖29-1),來分析設計思維上的差異。那麼,當開發者接到這個任務時,會如何著手呢?
ActionResult
回應ActionResult
回應ActionResult
回應store.ts
在設計觀點上,前後端分離 特別重視「兩個功能間如何切換」,這會直接影響到共用 store
的設計方式。
透過直接存取共用的 store,可以實現 部分畫面即時更新,不僅提供更好的使用體驗,也提升了共用元件的彈性。
反觀 傳統 MVC,不同的 View 之間通常僅能依靠 TempData
、Query String
或 Route Data
傳遞少量非敏感資料,來完成頁面間的串接。
雖然 MVC 提供了 Layout
(整體骨架)與 Partial View
(區塊重複利用)的機制,但最終仍是由伺服器產生一份完整的 HTML 回傳。
這種模式下,若僅有部分元素不同,往往需要再製作一份新的 Partial View
,邏輯插入與差異化設計的空間有限,也難以進一步抽象成真正可重用的元件。
相較之下,Vue 的 動態渲染 機制可以靈活處理差異,並讓前端以更細緻的方式控制畫面更新,兩者之間仍有顯著差距。
面向 | MVC (Razor) | 前後端分離 (SPA) |
---|---|---|
功能拆分 | 查詢、編輯各自獨立 Controller 與 View | 查詢、編輯各自獨立 Vue 元件 |
功能關聯 | 查詢 → 導頁至編輯頁 (帶 ID) | 查詢 → 點擊後於彈窗直接編輯 |
資料流 | Controller 負責撈資料 + 傳遞至 View | 前端透過 store.ts 管理共用 DTO,API 取資料 |
畫面開發 | View 綁定 Model,使用 Razor + HTML 繪製 | Vue 元件化開發,UI 與狀態解耦 |
互動模式 | 頁面跳轉式,功能邏輯與畫面耦合 | 單頁互動式,狀態驅動畫面更新 |
後端設計 | ActionResult 處理查詢/編輯/刪除邏輯 | 提供 API action,單純資料存取與邏輯 |
前後端整合 | 前端依賴後端輸出完整 HTML | 前端發送 API 請求,後端回傳 JSON |
使用體驗 | 每次操作需換頁,流程較中斷 | 流暢無刷新體驗,互動性更好 |
面向 | MVC Partial View | Vue 元件 (Component) |
---|---|---|
渲染流程 | Controller 產生 Model → Razor 產生 HTML → 回傳整頁或片段 | Component 內部 State/Props → Virtual DOM → 對比更新 → 更新 DOM |
資料流 | 單向:Controller → View → HTML → Browser | 雙向 / 單向:Props 從父元件傳入 → Component 內部 State 可雙向綁定 (v-model) |
生命週期 | 只在伺服器端請求時生成 HTML | 元件有完整生命周期 (created, mounted, updated, destroyed),可動態更新 DOM |
互動更新 | 若要更新,通常需送表單或再發請求 (Post/Redirect) | 可動態更新畫面,無需整頁刷新,事件驅動或響應式自動更新 |
以「休假申請單」為案例,表單開啟後通常會呈現申請的相關內容,不僅包含休假資訊,還需要顯示簽核歷程。
其中有一個區塊是 簽核流程。假設現在要新增一位簽核人員,如果希望在 點擊新增後只更新簽核清單區塊,而不是重新整個頁面(傳統 form post 的做法),那應該怎麼設計呢?
圖29-2:以請假單表單為範例,其中的簽核資訊在新增簽核人員後,需要能即時刷新更新。
ajax-post
發送增加簽核的請求。FlowSignList
的元素中完成動態更新內容,完成即時刷新需求。$.ajax({
type: "post", //使用 HTTP POST 方法發送請求
url: getAPFunPath("Flow", "AddFlowSign"),
data: JSON.stringify({
"strFlowID": keys[0],
"strSignID": keys[1],
"strAddSignUser": addUserID,
}),
async: false,
contentType: 'application/json; charset=utf-8',
success: function (data) {
if (data.indexOf("alert") > 0) {
$("#error-div").html(data);
} else {
/*
會將 AJAX 回傳的 data(通常是 HTML 片段)直接插入到網頁上 ID 為 AttachFileList 的元素中達到動態更新內容的標準做法,常用於檔案列表、表格、訊息等即時刷新
*/
$("#FlowSignList").html(data); // 會將 AJAX 回傳的 data 插入到網頁上 ID 為 AttachFileList 的元素
alert("新增成功");
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("error:AddFlowSign 有誤,請聯繫系統管理員,errorThrown : " + errorThrown);
}
});
雖然滿足了部分畫面的即時更新,但仍然有以下缺點:
flowSignStore.ts
addNewUser()
flowSignList
flowSignStore.ts
flowSignStore.addNewUser
flowSignStore.flowSignList
//flowSignStore.ts
import { fetchWrapper , type ApiRspMessage ,type ActionRsp } from '@/utils/helpers/fetch-wrapper';
const FlowSignList as FlowSignListDto[];
export const useflowSignStore= defineStore({
id: 'flowSignStore',
state: () => ({
flowSignList: [] as flowSignListDto[],
}),
actions: {
async addNewUser(userId: string) {
try {
const respData: ActionRsp [] = await fetchWrapper.post(apiURL,userId) as ActionRsp [];
this.flowSignList = respData;
}catch (error) {
if (error instanceof Response) {
const errorText = await error.text();
console.error('Failed to create software:', errorText);
} else {
console.error('Failed to create software:', error);
}
}
}
});
雖然一開始需要先開發元件或額外建立 Store,但後續就能重複使用,讓共用性最大化。不僅維護性更高、可讀性也大幅提升,更重要的是:透過自動追蹤資料狀態即可觸發畫面更新,無需額外撰寫事件或條件判斷來決定是否要刷新畫面,這點相當省事且值得投入。
以範例來說,當使用者點擊按鈕觸發事件並完成作業後,畫面能自動更新;若沒有這樣的設計,通常就只能依靠 setInterval
等定時機制來達成。
面向 | 傳統作法 (MVC / jQuery) | Vue 作法 (前後端分離 + Store) |
---|---|---|
畫面更新方式 | 透過整頁 Postback 或重新載入 HTML | 透過 響應式資料綁定,自動更新部分畫面區塊 |
元件/邏輯共用性 | 低,常需重複撰寫或複製多份 Partial View | 高,可將元件抽離並搭配 Store 重複使用 |
資料狀態追蹤 | 需要手動判斷與事件監控(e.g. callback, 判斷 flag) | Vue 透過 響應式系統 自動追蹤狀態並刷新畫面 |
額外處理機制 | 常需用 setInterval 或手動觸發刷新 |
無需額外事件,資料變動即自動觸發畫面渲染 |
維護性 / 可讀性 | 較低,程式碼分散且重複 | 較高,結構清晰且能快速定位問題 |
總結上述的分析,前後端分離後,開發與設計上的思維有著以下的不同:
- 功能切割上面需要考量更細緻,更注重再不同介面(功能)間的介面串接方式,包含共用 Store 與不同 vue 參數 props 傳遞、更細緻如props 傳遞物件結構也需要好好規劃。
- 開發會朝向高度共用與元件化:收到開發任務時不是僅考慮單一功能的完成,而去思考有哪些功能可再元件化、哪些可共用的方向開始著手,團隊中開發者,著手前先了解有哪些元件可以使用。
- Vue 前後端分離的架構下,前端(Vue)主要負責資料的呈現、使用者互動和介面邏輯,盡量避免處理複雜的業務邏輯、權限判斷、資料整理與重組。後端 API 專心負責負責資料存取、業務邏輯、權限驗證等核心功能。