12 KiB
前端分層規則與目前結構
目的
這份文件描述目前 repo 已經落地的前端分層與命名規則,讓後續新增檔案、搬移檔案或重構時有一致判斷基準。
本文件是現況快照;新增功能與重構的細節規範以 docs/architecture-strategy.md、docs/llm-development-guide.md 與各層 src/**/GUIDE.md 為準。
目前專案的主要責任鏈如下:
router -> App.vue -> AppShell -> layout -> view -> page component -> section -> item
↓
page driver / command composable -> store -> service
目前目錄的責任邊界
src/router
目前路由集中在:
責任:
- 定義 route 與 route meta。
- 指定頁面使用哪種 layout。
- 串接導航守衛。
目前 meta.layout 是 app shell 切換的正式入口:
default走 MainLayout.vue。none走 PlainLayout.vue。
src/App.vue 與 src/shell
App.vue 目前只掛載 AppShell.vue,不再直接承擔全域 UI 組裝。
src/shell 是 App Shell 層:
- AppShell.vue:layout 切換、layout props/events、breadcrumb actions、tabs router-view 與
GlobalOverlays掛載。 - AppTabs.vue:default layout 下的 tabs 與 keep-alive router-view 容器。
- GlobalOverlays.vue:全域 snackbar、搜尋 dialog、訊息 dialog。
判斷原則:
- 與整個 app shell 共享、且不屬於單一路由頁面的 UI,可放在
src/shell。 - 只屬於單一路由頁面的對話框或互動,不應放進
src/shell。 - shell 狀態協調優先放在
src/composables/layout/useAppShell.ts。
src/views
views 是 route entry,方向是薄層:呼叫 page driver、掛載 page component、協調 route-level 事件。
目前較典型的薄 view:
- Home.vue
- Settings.vue
- FncPage.vue
- SingleRecord.vue
- EditableGrid.vue
- MasterDetailA.vue
- MasterDetailB.vue
- MasterDetailC.vue
錯誤頁集中在 src/views/errors,通常使用 meta.layout = 'none',並由 ErrorShell.vue 共用錯誤頁骨架。
Login.vue 是 template core 例外:它仍負責登入頁組合、功能開關、小型提示 dialog 與登入流程協調。登入頁 UI 拆在 components/login/*,captcha 與 announcement 流程拆在頂層 login composable。
views 應遵守的原則:
- 可以持有 route、page driver 掛載、頁面資料組裝與頁面事件協調。
- 可以管理只屬於該頁的小型 dialog 顯示狀態。
- 不應長期承擔大型表格、表單、dialog 模板或可重用流程。
- 不應直接處理底層 HTTP 細節。
src/components
components 依角色分層,不再用單一規則描述。
1. Root page/template components
目前仍放在 src/components 根目錄的頁面外殼:
這些是既有 template 頁面外殼或登入頁組裝元件。新增一般功能頁時,優先使用 src/components/pages。
2. components/pages
components/pages 是完整頁面主畫面組裝層:
- PageHome.vue
- PageSettings.vue
- PageFunction.vue
- PageMaintenance.vue
- PageEditableGridMaintenance.vue
- PageMasterDetailAMaintenance.vue
- PageMasterDetailBMaintenance.vue
- PageMasterDetailCMaintenance.vue
責任:
- 接收 view/page driver 組好的資料與事件。
- 組裝完整頁面的主要 section 順序。
- 再往下使用 sections、items、feature/domain components。
3. components/sections
components/sections 是頁面區塊容器:
- SectionSearchPanel.vue
- SectionDataTable.vue
- SectionFormPanel.vue
- SectionFormPage.vue
- SectionQueryPage.vue
責任:
- 決定區塊布局與區塊互動。
- 以 props 接收資料,以 emit 回報事件。
- 不知道 route,不直接呼叫 API。
4. components/items
components/items 是欄位群組或單筆資料呈現層:
item 不應知道自己被放在表格、grid、dialog 或頁面哪個位置。
5. components/login
登入頁的較細 UI 區塊集中在:
- CreateAccountLink.vue
- LoginAnnouncementBoard.vue
- LoginBrand.vue
- LoginForm.vue
- LoginHeader.vue
- LoginIllustration.vue
- LoginToolBar.vue
- LoginVerify.vue
這一層服務 PageLogin,不是全域 base library。
6. components/maint
components/maint 是 maintenance demo / domain component 區域:
- CommonConfirmDialog.vue
- EditableGrid.vue
- MasterFileFormFields.vue
- MntDialogCard.vue
- MntRecordNavToolbar.vue
master-detail/*
若只是維護頁專用子元件,不要搬到 base。
7. components/layouts
layout 實作集中於:
- MainLayout.vue
- PlainLayout.vue
src/components/layouts/main-layout/*
layout 只承擔 app shell、drawer、app bar、favorites、breadcrumb 等框架 UI,不承擔頁面專屬業務流程。
8. components/base
components/base 放真正跨頁共用且不屬於特定 domain 的基礎元件:
只服務單一頁面家族或單一 domain 的元件不要放進 base。
src/composables
目前 composables 分成:
page-drivers/*:頁面資料協調與 page model 組裝。commands/*:命令式副作用流程,例如 create/edit/save/delete。layout/*:AppShell / layout 狀態與事件協調。maint/*:maintenance demo 的表單、CRUD、editable grid 狀態。- 頂層 login / utility composable:
useLoginCaptcha.ts、useLoginAnnouncements.ts、useApiCall.ts。
責任:
- 放可重用流程。
- 放可測試的 UI state。
- 放與模板結構耦合較低的狀態機。
- 不 import component 或 view。
src/stores
目前 store 是跨頁共享狀態、快取與全域顯示狀態的正式分層。
代表性檔案:
責任:
- 承接跨頁共享狀態。
- 承接畫面快取與全域顯示狀態。
- 作為 view/page driver/composable 與 services 之間的狀態收斂點。
app.ts 目前是空的 Pinia scaffold,尚未承擔實際 app state。
src/services
services 是 HTTP 與外部 API 邊界。
代表性檔案:
- client.ts
- interceptors.ts
- error.ts
- http-error.ts
- http-toast.ts
- session.ts
- token.ts
services/modules/*
責任:
- 提供
httpClient。 - 封裝 API 模組。
- 統一 token、session 與錯誤處理。
規則:
- 元件不直接處理底層 HTTP 細節。
- service module 不持有 UI 狀態。
- 可共享的請求流程優先收斂到 store、page driver 或 composable,再由它們呼叫 service。
目前已落地的分層模式
模式 1:view -> page driver -> page component
已落地頁面:
HomeSettingsFncPageviews/maint/*
穩定模式:
- view 負責掛載 page driver 與 page component。
- page driver 負責 page model、事件與頁面狀態協調。
- page component 負責頁面主畫面組裝。
模式 2:Login.vue -> PageLogin -> login components/composables
登入頁是 template core,功能開關集中在 Login.vue:
withCaptchawithAnnouncementwithForgotPasswordwithRememberAccount
資料流與 side effect 分別由 useLoginCaptcha()、useLoginAnnouncements()、PageLogin 與 LoginForm 承接。
模式 3:router meta -> AppShell -> layout
這一層已正式成立:
- route 決定 layout 類型。
AppShell決定套用哪個 shell layout。- layout 專注在骨架與共用框架 UI。
命名規則
頁面與 page component
- 直接被 route 載入的檔案放
views。 - 負責完整頁面畫面組裝的元件,檔名用
Page前綴。 - page component 優先放
components/pages;既有 template 外殼可保留在components根目錄。 - page component 不放進
base。
資料夾命名
- 多字資料夾一律使用
kebab-case。 - 不新增
snake_case或PascalCase資料夾。
目前例子:
main-layoutmaster-detailpage-drivers
component 命名
- Page component:
PageXxx.vue - Section component:
SectionXxx.vue - Item component:
ItemXxx.vue - Base component:不使用
Page/Section/Item前綴,直接以功能命名。
新增或修改檔案時的判斷準則
- 這個檔案是否直接被 route 載入?
- 是:優先放
views。
- 是:優先放
- 這個檔案是否負責某個完整頁面的主畫面組裝?
- 是:用
Page前綴,優先放components/pages,不要塞進base。
- 是:用
- 這段重複的是模板還是流程?
- 模板:抽元件。
- 流程:抽 composable、page driver、command 或 store。
- 這個狀態是否跨頁共享,或需要快取 / 全域顯示控制?
- 是:優先考慮 store。
- 這個邏輯是否在處理 API、token、session、錯誤正規化?
- 是:放
services。
- 是:放
- 這個元件是否只屬於單一 domain 或單一頁面家族?
- 是:優先放到該 domain / feature 目錄,例如
components/maint或components/login。
- 是:優先放到該 domain / feature 目錄,例如
- 這個抽象是否真的降低重複與理解成本?
- 否:不要抽。