13 KiB
前端分層規則與目前結構
目的
這份文件用來同步目前專案前端分層的實際狀況,讓新增功能、搬移檔案、或拆分元件時有一致判斷基準。
這份文件有兩個用途:
- 描述目前已落地的結構與責任邊界
- 明確標示哪些地方仍是過渡狀態,避免把「現況」誤認成「目標架構」
目前專案的主要分層不是只有 views / components / composables / layouts,而是已經形成以下責任鏈:
router決定 route 與 layout metaApp.vue根據 route meta 組裝 app shell 與全域 UIviews承接路由入口與頁面資料組裝components承接畫面組裝、頁面型元件、功能區塊與基礎元件composables承接可重用流程與 UI statestores承接跨頁狀態、快取與 UI 顯示狀態services承接 HTTP client、API 模組、token 與錯誤正規化
目前目錄的責任邊界
src/router
目前路由集中在:
責任:
- 定義 route 與 route meta
- 指定頁面使用哪種 layout
- 串接導航守衛
目前 meta.layout 已經成為 app shell 切換的正式入口,這一點會影響 App.vue 與 layout 的責任切分。
src/App.vue
App.vue 目前不是單純的掛載殼層,而是實際的應用組裝層。
目前承擔的責任包含:
- 依
route.meta.layout切換SKAdminLayout與SKEmptyLayout - 組裝 breadcrumb / favorites / menu 等 layout props
- 放置全域搜尋結果 dialog
- 放置全域訊息中心 dialog
- 放置全域 snackbar
- 串接 layout event 與路由跳轉
判斷原則:
- 與「整個 app shell」共享、且不屬於單一頁面的 UI,可留在
App.vue - 只屬於單一路由頁面的對話框或互動,不應再往
App.vue堆
src/views
views 目前整體方向是「路由入口 + 頁面組裝」,但尚未完全一致。
目前已接近薄 view 的頁面:
- EditableGrid.vue
- Forbidden.vue
- ServerError.vue
- ServiceUnavailable.vue
- NetworkError.vue
- Maintenance.vue
- NotFound.vue
- FncPage.vue
目前仍偏厚的頁面:
目前 views 應遵守的原則:
- 可以持有 route、store、頁面資料組裝、頁面事件協調
- 不應長期承擔大量可拆出的模板片段
- 不應把可重用流程直接留在頁面內反覆複製
src/components
目前 components 已經分成三種主要角色,不能再用單一規則描述。
1. 頁面型元件
目前以下元件實際上扮演「page component」:
這些檔案目前的實際責任是:
- 接收 view 組好的資料
- 組裝頁面主畫面
- 再往下使用較小的子元件
這代表它們不是 base,也不是 layout,而是現行專案的頁面型組裝層。
2. components/base
文件原先寫法是「base 只放真正通用元件」,但目前專案並不完全符合。
現在 components/base 內主要保留頁面家族的內部子元件:
base/login/*
因此目前對 base 的正確認知應該是:
- 它已經不是純粹的「全域 base library」
- 它同時包含「通用元件」與「頁面型元件拆出的子元件」
- 新增檔案時不要因為方便就繼續把所有頁面子元件丟進
base
目前建議:
- 若元件只服務單一頁面家族,優先放到對應資料夾或 feature/domain 資料夾
3. components/layouts
目前 layout 相關實作集中於:
- SKAdminLayout.vue
- SKEmptyLayout.vue
- SKMainLayout.vue
- SKSimpleLayout.vue
src/components/layouts/sk-admin-layout/*
目前主實作仍以 SKAdminLayout 為核心,子元件拆分集中在 sk-admin-layout。
layout 應只承擔:
- app shell
- drawer / app bar / favorites / breadcrumb 等框架 UI
- 與 layout 視覺結構直接相關的互動
layout 不應承擔:
- 頁面專屬業務流程
- 特定 domain 的資料規則
3. components/maint
這個目錄目前已經是最接近 feature folder 的區域:
- CommonConfirmDialog.vue
- EditableStudentGrid.vue
- MasterFormFields.vue
- MntDialogCard.vue
- MntRecordNavToolbar.vue
- PageMaint.vue
master-detail/*
結論:
components/maint主要扮演 maintenance domain component 層- 像
ConfirmDialog這類通用確認視窗可以直接在頁面使用,不需要再包一層 page-specific dialog aggregator - 目前沒有必要為了形式硬搬到
components/features/maintenance
src/composables
目前已明確分成兩組:
composables/layout/*composables/maintenance/*
代表性檔案:
- useAdminLayoutState.ts
- useThemeToggle.ts
- useMaintenanceCrudFlow.ts
- useStudentMaintenanceForm.ts
- useEditableStudentGrid.ts
- useApiCall.ts
目前 composables 的責任是正確的:
- 放可重用流程
- 放可測試的 UI state
- 放與模板結構耦合較低的狀態機
src/stores
目前 store 已經成為正式分層的一部分,而不只是暫時狀態容器。
代表性檔案:
- auth.ts
- menu.ts
- breadcrumbs.ts
- favorites.ts
- messages.ts
- snackbar.ts
- loginAnnouncements.ts
- students.ts
- semesters.ts
責任:
- 承接跨頁共享狀態
- 承接畫面快取與顯示狀態
- 作為 view 與 services 之間的狀態收斂點
注意:
- 目前存在
src/stores/stores/*的重複目錄,這不是分層設計的一部分,應視為待整理結構噪音
src/services
services 現在已經是一層明確的資料存取邊界,不應再被視為附屬工具資料夾。
代表性檔案:
- client.ts
- interceptors.ts
- error.ts
- http-error.ts
- http-toast.ts
- session.ts
- token.ts
services/modules/*
責任:
- 提供 HTTP client
- 封裝 API 模組
- 統一 token、session 與錯誤處理
規則:
- 元件不直接處理底層 HTTP 細節
- 可共享的請求流程優先收斂到 store 或 composable,再由它們呼叫 service
目前已落地的分層模式
模式 1:view -> page component -> base 子元件
已落地頁面:
LoginHome
代表目前專案已經存在一種穩定模式:
view負責資料準備與事件協調- page component 負責頁面畫面組裝
- 較細的視覺區塊再拆到內部子元件
這是目前在一般展示頁面中最明確的頁面分層。
模式 2:view -> page-level maintenance component + maintenance components + maintenance composables
已落地區域:
views/maint/*components/maint/*composables/maintenance/*
其中 EditableGrid.vue 是目前最接近目標狀態的 maintenance 頁面。
模式 3:router meta -> App.vue -> layout
這一層目前已正式成立:
- route 決定 layout 類型
App.vue決定套用哪個 shell- layout 專注在骨架與共用框架 UI
這代表 layout 的責任邊界不應再回頭混入頁面內部流程。
現況偏差與文件修正
以下是文件與實際程式碼之間原本不一致的地方,這次同步後已改成以現況描述:
components/base並非只放真正通用元件- 專案目前除了
login / home的view -> page component模式外,maintenance也已形成頁面級元件 + domain component 的模式 stores與services已經是正式架構層,不能繼續省略不寫App.vue目前實際承擔全域 UI 組裝責任,不能只把它視為掛載入口- 錯誤頁已明確收斂到
views/errors/*
目前仍待整理的區域
高優先度
原因:
- 這些頁面仍保留大量資料轉換或頁面內對話框協調邏輯
中優先度
- 重新命名或重新安置頁面型元件:
原因:
- 它們仍是頁面型元件,但目前放在
components根目錄 - 若後續繼續保留 page component 分層,建議改成更清楚的命名與歸位方式
中低優先度
- 清理
src/stores/stores/*重複結構 - 檢查
components/base/*是否要把頁面家族子元件搬到更明確的資料夾 - 檢查空資料夾
src/components/base/management是否仍需要保留
新增或修改檔案時的判斷準則
- 這個檔案是否直接被 route 載入?
- 是:優先放
views
- 是:優先放
- 這個檔案是否負責某個完整頁面的主畫面組裝?
- 是:放頁面型元件層,且命名以
Page為前綴,不要塞進base
- 是:放頁面型元件層,且命名以
- 這段重複的是模板還是流程?
- 模板:抽元件
- 流程:抽 composable 或 store
- 這個狀態是否跨頁共享,或需要快取 / 全域顯示控制?
- 是:優先考慮 store
- 這個邏輯是否在處理 API、token、session、錯誤正規化?
- 是:放
services
- 是:放
- 這個元件是否只屬於單一 domain?
- 是:優先放到該 domain 目錄,例如
components/maint
- 是:優先放到該 domain 目錄,例如
- 這個抽象是否真的降低重複與理解成本?
- 否:不要抽