Files
skt-vuetify-templates/docs/frontend-layering.md
T
2026-03-30 10:08:24 +08:00

337 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 前端分層規則與目前結構
## 目的
這份文件用來同步目前專案前端分層的實際狀況,讓新增功能、搬移檔案、或拆分元件時有一致判斷基準。
這份文件有兩個用途:
- 描述目前已落地的結構與責任邊界
- 明確標示哪些地方仍是過渡狀態,避免把「現況」誤認成「目標架構」
目前專案的主要分層不是只有 `views / components / composables / layouts`,而是已經形成以下責任鏈:
- `router` 決定 route 與 layout meta
- `App.vue` 根據 route meta 組裝 app shell 與全域 UI
- `views` 承接路由入口與頁面資料組裝
- `components` 承接畫面組裝、頁面型元件、功能區塊與基礎元件
- `composables` 承接可重用流程與 UI state
- `stores` 承接跨頁狀態、快取與 UI 顯示狀態
- `services` 承接 HTTP client、API 模組、token 與錯誤正規化
## 目前目錄的責任邊界
### `src/router`
目前路由集中在:
- [routes.ts](/home/carl/git/skt-vuetify-templates/src/router/routes.ts)
- [index.ts](/home/carl/git/skt-vuetify-templates/src/router/index.ts)
- [guards.ts](/home/carl/git/skt-vuetify-templates/src/router/guards.ts)
責任:
- 定義 route 與 route meta
- 指定頁面使用哪種 layout
- 串接導航守衛
目前 `meta.layout` 已經成為 app shell 切換的正式入口,這一點會影響 `App.vue` 與 layout 的責任切分。
### `src/App.vue`
[App.vue](/home/carl/git/skt-vuetify-templates/src/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 的頁面:
- [EditableGridMnt.vue](/home/carl/git/skt-vuetify-templates/src/views/maint/EditableGridMnt.vue)
- [Forbidden.vue](/home/carl/git/skt-vuetify-templates/src/views/errors/Forbidden.vue)
- [ServerError.vue](/home/carl/git/skt-vuetify-templates/src/views/errors/ServerError.vue)
- [ServiceUnavailable.vue](/home/carl/git/skt-vuetify-templates/src/views/errors/ServiceUnavailable.vue)
- [NetworkError.vue](/home/carl/git/skt-vuetify-templates/src/views/errors/NetworkError.vue)
- [Maintenance.vue](/home/carl/git/skt-vuetify-templates/src/views/errors/Maintenance.vue)
- [NotFound.vue](/home/carl/git/skt-vuetify-templates/src/views/errors/NotFound.vue)
- [FncPage.vue](/home/carl/git/skt-vuetify-templates/src/views/FncPage.vue)
目前仍偏厚的頁面:
- [Login.vue](/home/carl/git/skt-vuetify-templates/src/views/Login.vue)
- [Home.vue](/home/carl/git/skt-vuetify-templates/src/views/Home.vue)
- [SingleRecordMnt.vue](/home/carl/git/skt-vuetify-templates/src/views/maint/SingleRecordMnt.vue)
- [MasterDetailMnt.vue](/home/carl/git/skt-vuetify-templates/src/views/maint/MasterDetailMnt.vue)
- [MasterDetailMntB.vue](/home/carl/git/skt-vuetify-templates/src/views/maint/MasterDetailMntB.vue)
- [MasterDetailMntC.vue](/home/carl/git/skt-vuetify-templates/src/views/maint/MasterDetailMntC.vue)
目前 `views` 應遵守的原則:
- 可以持有 route、store、頁面資料組裝、頁面事件協調
- 不應長期承擔大量可拆出的模板片段
- 不應把可重用流程直接留在頁面內反覆複製
### `src/components`
目前 `components` 已經分成三種主要角色,不能再用單一規則描述。
#### 1. 頁面型元件
目前以下元件實際上扮演「page component」:
- [PageLogin.vue](/home/carl/git/skt-vuetify-templates/src/components/PageLogin.vue)
- [PageIndex.vue](/home/carl/git/skt-vuetify-templates/src/components/PageIndex.vue)
這些檔案目前的實際責任是:
- 接收 view 組好的資料
- 組裝頁面主畫面
- 再往下使用較小的子元件
這代表它們不是 `base`,也不是 layout,而是現行專案的頁面型組裝層。
#### 2. `components/base`
文件原先寫法是「`base` 只放真正通用元件」,但目前專案並不完全符合。
現在 `components/base` 內主要保留頁面家族的內部子元件:
- `base/login/*`
因此目前對 `base` 的正確認知應該是:
- 它已經不是純粹的「全域 base library」
- 它同時包含「通用元件」與「頁面型元件拆出的子元件」
- 新增檔案時不要因為方便就繼續把所有頁面子元件丟進 `base`
目前建議:
- 若元件只服務單一頁面家族,優先放到對應資料夾或 feature/domain 資料夾
#### 3. `components/layouts`
目前 layout 相關實作集中於:
- [SKAdminLayout.vue](/home/carl/git/skt-vuetify-templates/src/components/layouts/SKAdminLayout.vue)
- [SKEmptyLayout.vue](/home/carl/git/skt-vuetify-templates/src/components/layouts/SKEmptyLayout.vue)
- [SKMainLayout.vue](/home/carl/git/skt-vuetify-templates/src/components/layouts/SKMainLayout.vue)
- [SKSimpleLayout.vue](/home/carl/git/skt-vuetify-templates/src/components/layouts/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/maintenance`
這個目錄目前已經是最接近 feature folder 的區域:
- [EditableStudentGrid.vue](/home/carl/git/skt-vuetify-templates/src/components/maintenance/EditableStudentGrid.vue)
- [MaintenanceCrudDialogs.vue](/home/carl/git/skt-vuetify-templates/src/components/maintenance/MaintenanceCrudDialogs.vue)
- [MaintenanceStudentFormFields.vue](/home/carl/git/skt-vuetify-templates/src/components/maintenance/MaintenanceStudentFormFields.vue)
- [MntDialogCard.vue](/home/carl/git/skt-vuetify-templates/src/components/maintenance/MntDialogCard.vue)
- [MntPageCards.vue](/home/carl/git/skt-vuetify-templates/src/components/maintenance/MntPageCards.vue)
- [MntRecordNavToolbar.vue](/home/carl/git/skt-vuetify-templates/src/components/maintenance/MntRecordNavToolbar.vue)
- `master-detail/*`
- `master-detail-b/*`
- `master-detail-c/*`
結論:
- `components/maintenance` 已實質扮演 domain component 層
- 目前沒有必要為了形式硬搬到 `components/features/maintenance`
### `src/composables`
目前已明確分成兩組:
- `composables/layout/*`
- `composables/maintenance/*`
代表性檔案:
- [useAdminLayoutState.ts](/home/carl/git/skt-vuetify-templates/src/composables/layout/useAdminLayoutState.ts)
- [useThemeToggle.ts](/home/carl/git/skt-vuetify-templates/src/composables/layout/useThemeToggle.ts)
- [useMaintenanceCrudFlow.ts](/home/carl/git/skt-vuetify-templates/src/composables/maintenance/useMaintenanceCrudFlow.ts)
- [useStudentMaintenanceForm.ts](/home/carl/git/skt-vuetify-templates/src/composables/maintenance/useStudentMaintenanceForm.ts)
- [useEditableStudentGrid.ts](/home/carl/git/skt-vuetify-templates/src/composables/maintenance/useEditableStudentGrid.ts)
- [useApiCall.ts](/home/carl/git/skt-vuetify-templates/src/composables/useApiCall.ts)
目前 `composables` 的責任是正確的:
- 放可重用流程
- 放可測試的 UI state
- 放與模板結構耦合較低的狀態機
### `src/stores`
目前 store 已經成為正式分層的一部分,而不只是暫時狀態容器。
代表性檔案:
- [auth.ts](/home/carl/git/skt-vuetify-templates/src/stores/auth.ts)
- [menu.ts](/home/carl/git/skt-vuetify-templates/src/stores/menu.ts)
- [breadcrumbs.ts](/home/carl/git/skt-vuetify-templates/src/stores/breadcrumbs.ts)
- [favorites.ts](/home/carl/git/skt-vuetify-templates/src/stores/favorites.ts)
- [messages.ts](/home/carl/git/skt-vuetify-templates/src/stores/messages.ts)
- [snackbar.ts](/home/carl/git/skt-vuetify-templates/src/stores/snackbar.ts)
- [loginAnnouncements.ts](/home/carl/git/skt-vuetify-templates/src/stores/loginAnnouncements.ts)
- [students.ts](/home/carl/git/skt-vuetify-templates/src/stores/students.ts)
- [semesters.ts](/home/carl/git/skt-vuetify-templates/src/stores/semesters.ts)
責任:
- 承接跨頁共享狀態
- 承接畫面快取與顯示狀態
- 作為 view 與 services 之間的狀態收斂點
注意:
- 目前存在 `src/stores/stores/*` 的重複目錄,這不是分層設計的一部分,應視為待整理結構噪音
### `src/services`
`services` 現在已經是一層明確的資料存取邊界,不應再被視為附屬工具資料夾。
代表性檔案:
- [client.ts](/home/carl/git/skt-vuetify-templates/src/services/client.ts)
- [interceptors.ts](/home/carl/git/skt-vuetify-templates/src/services/interceptors.ts)
- [error.ts](/home/carl/git/skt-vuetify-templates/src/services/error.ts)
- [http-error.ts](/home/carl/git/skt-vuetify-templates/src/services/http-error.ts)
- [http-toast.ts](/home/carl/git/skt-vuetify-templates/src/services/http-toast.ts)
- [session.ts](/home/carl/git/skt-vuetify-templates/src/services/session.ts)
- [token.ts](/home/carl/git/skt-vuetify-templates/src/services/token.ts)
- `services/modules/*`
責任:
- 提供 HTTP client
- 封裝 API 模組
- 統一 token、session 與錯誤處理
規則:
- 元件不直接處理底層 HTTP 細節
- 可共享的請求流程優先收斂到 store 或 composable,再由它們呼叫 service
## 目前已落地的分層模式
### 模式 1`view -> page component -> base 子元件`
已落地頁面:
- `Login`
- `Home`
代表目前專案已經存在一種穩定模式:
- `view` 負責資料準備與事件協調
- page component 負責頁面畫面組裝
- 較細的視覺區塊再拆到內部子元件
這是目前在一般展示頁面中最明確的頁面分層。
### 模式 2`view -> maintenance components + maintenance composables`
已落地區域:
- `views/maint/*`
- `components/maintenance/*`
- `composables/maintenance/*`
其中 [EditableGridMnt.vue](/home/carl/git/skt-vuetify-templates/src/views/maint/EditableGridMnt.vue) 是目前最接近目標狀態的 maintenance 頁面。
### 模式 3`router meta -> App.vue -> layout`
這一層目前已正式成立:
- route 決定 layout 類型
- `App.vue` 決定套用哪個 shell
- layout 專注在骨架與共用框架 UI
這代表 layout 的責任邊界不應再回頭混入頁面內部流程。
## 現況偏差與文件修正
以下是文件與實際程式碼之間原本不一致的地方,這次同步後已改成以現況描述:
- `components/base` 並非只放真正通用元件
- 專案目前不只 maintenance 有分層,`login / dashboard / analysis / management` 也已形成 `view -> page component` 模式
- `stores``services` 已經是正式架構層,不能繼續省略不寫
- `App.vue` 目前實際承擔全域 UI 組裝責任,不能只把它視為掛載入口
- 錯誤頁已明確收斂到 `views/errors/*`
## 目前仍待整理的區域
### 高優先度
- 繼續瘦身:
- [SingleRecordMnt.vue](/home/carl/git/skt-vuetify-templates/src/views/maint/SingleRecordMnt.vue)
- [MasterDetailMnt.vue](/home/carl/git/skt-vuetify-templates/src/views/maint/MasterDetailMnt.vue)
- [MasterDetailMntB.vue](/home/carl/git/skt-vuetify-templates/src/views/maint/MasterDetailMntB.vue)
- [MasterDetailMntC.vue](/home/carl/git/skt-vuetify-templates/src/views/maint/MasterDetailMntC.vue)
- 重新整理:
- [Login.vue](/home/carl/git/skt-vuetify-templates/src/views/Login.vue)
原因:
- 這些頁面仍保留大量資料轉換或頁面內對話框協調邏輯
### 中優先度
- 重新命名或重新安置頁面型元件:
- [PageLogin.vue](/home/carl/git/skt-vuetify-templates/src/components/PageLogin.vue)
- [PageIndex.vue](/home/carl/git/skt-vuetify-templates/src/components/PageIndex.vue)
原因:
- 它們仍是頁面型元件,但目前放在 `components` 根目錄
- 若後續繼續保留 page component 分層,建議改成更清楚的命名與歸位方式
### 中低優先度
- 清理 `src/stores/stores/*` 重複結構
- 檢查 `components/base/*` 是否要把頁面家族子元件搬到更明確的資料夾
- 檢查空資料夾 `src/components/base/management` 是否仍需要保留
## 新增或修改檔案時的判斷準則
1. 這個檔案是否直接被 route 載入?
- 是:優先放 `views`
2. 這個檔案是否負責某個完整頁面的主畫面組裝?
- 是:放頁面型元件層,不要塞進 `base`
3. 這段重複的是模板還是流程?
- 模板:抽元件
- 流程:抽 composable 或 store
4. 這個狀態是否跨頁共享,或需要快取 / 全域顯示控制?
- 是:優先考慮 store
5. 這個邏輯是否在處理 API、token、session、錯誤正規化?
- 是:放 `services`
6. 這個元件是否只屬於單一 domain?
- 是:優先放到該 domain 目錄,例如 `components/maintenance`
7. 這個抽象是否真的降低重複與理解成本?
- 否:不要抽