Files
skt-vuetify-templates/docs/frontend-layering.md
T
2026-03-30 15:04:27 +08:00

16 KiB
Raw Blame History

前端分層規則與目前結構

目的

這份文件只描述目前 repo 已經落地的前端分層與命名規則,讓後續新增檔案、搬移檔案、或重構時有一致判斷基準。

重點不是追求理想化架構,而是避免把舊名稱、過渡寫法、或已刪除的結構繼續當成規則。

目前專案的主要責任鏈如下:

  • router 決定 route 與 layout meta
  • App.vue 根據 route meta 組裝 app shell 與全域 UI
  • views 承接路由入口與頁面資料協調
  • components 承接 layout、page component、domain component 與較細的 UI 區塊
  • composables 承接可重用流程與 UI state
  • stores 承接跨頁狀態、快取與全域顯示狀態
  • services 承接 HTTP client、API 模組、token 與錯誤處理

目前目錄的責任邊界

src/router

目前路由集中在:

責任:

  • 定義 route 與 route meta
  • 指定頁面使用哪種 layout
  • 串接導航守衛

目前 meta.layout 已是 app shell 切換的正式入口:

src/App.vue

App.vue 目前不是單純掛載入口,而是實際的應用組裝層。

目前承擔的責任包含:

  • 根據 route.meta.layout 切換 layout
  • 組裝 breadcrumb / favorites / menu 等 layout props
  • 放置全域搜尋結果 dialog
  • 放置全域訊息中心 dialog
  • 放置全域 snackbar
  • 串接 layout event 與路由跳轉

判斷原則:

  • 與整個 app shell 共享、且不屬於單一路由頁面的 UI,可留在 App.vue
  • 只屬於單一路由頁面的對話框或互動,不應堆到 App.vue

src/views

views 目前整體方向是「路由入口 + 頁面資料協調 + 頁面事件協調」。

目前較薄的 view

目前仍偏厚的 view

views 應遵守的原則:

  • 可以持有 route、store、頁面資料組裝、頁面事件協調
  • 可以管理只屬於該頁的 dialog 顯示狀態
  • 不應長期承擔大量可抽出的模板片段
  • 不應把可重用流程直接留在頁面內重複複製

src/components

目前 components 已經分成幾種不同角色,不能再用單一規則描述。

1. 頁面型元件

目前以下元件實際上扮演 page component

這些檔案的責任是:

  • 接收 view 組好的資料與事件
  • 組裝某個完整頁面的主畫面
  • 再往下使用較小的子元件或 domain component

命名規則:

  • 只要是 page component,檔名以 Page 為前綴
  • page component 可以放在 components 根目錄
  • 不要把 page component 丟進 base

2. components/login

登入頁的較細 UI 區塊已集中到:

這一層的定位是:

  • 服務 PageLogin
  • 屬於 login 頁面家族
  • 不是全域 base library

3. components/base

目前 components/base 只剩下:

目前判斷原則很直接:

  • base 只放真正可跨頁重用、且不屬於特定 domain 的元件
  • 若元件只服務單一頁面家族或單一 domain,優先放回對應資料夾

4. components/layouts

目前 layout 實作集中於:

其中 sk-admin-layout/*MainLayout 底下拆出的骨架子元件:

layout 應只承擔:

  • app shell
  • drawer / app bar / favorites / breadcrumb 等框架 UI
  • 與 layout 視覺結構直接相關的互動

layout 不應承擔:

  • 頁面專屬業務流程
  • 特定 domain 的資料規則

5. components/maint

這個目錄目前是最接近 feature folder 的區域,放 maintenance 領域的 page component 與 domain component

master-detail/* 目前屬於維護頁專用的較細組件群:

結論:

  • components/maint 主要扮演 maintenance domain component 層
  • CommonConfirmDialog 可以直接在 maintenance 頁或元件使用,不需要再包一層 CRUD dialog aggregator
  • 若只是維護頁專用子元件,不要搬到 base

src/composables

目前已明確分成兩組:

  • composables/layout/*
  • composables/maint/*

代表性檔案:

composables 的責任:

  • 放可重用流程
  • 放可測試的 UI state
  • 放與模板結構耦合較低的狀態機

src/stores

目前 store 已經是正式分層的一部分,而不只是暫時狀態容器。

代表性檔案:

責任:

  • 承接跨頁共享狀態
  • 承接畫面快取與顯示狀態
  • 作為 view 與 services 之間的狀態收斂點

注意:

  • 目前仍存在 src/stores/stores/* 的重複目錄
  • 這不是分層設計的一部分,而是待整理的結構噪音

src/services

services 現在已經是一層明確的資料存取邊界,不應再被視為附屬工具資料夾。

代表性檔案:

責任:

  • 提供 HTTP client
  • 封裝 API 模組
  • 統一 token、session 與錯誤處理

規則:

  • 元件不直接處理底層 HTTP 細節
  • 可共享的請求流程優先收斂到 store 或 composable,再由它們呼叫 service

目前已落地的分層模式

模式 1view -> page component -> page family components

已落地頁面:

  • Login
  • Home

目前的穩定模式是:

  • view 負責資料準備與事件協調
  • page component 負責頁面主畫面組裝
  • 較細的視覺區塊再拆到對應頁面家族資料夾,例如 components/login/*

模式 2view -> page component / domain components + maint composables

已落地區域:

  • views/maint/*
  • components/maint/*
  • composables/maint/*

這一層目前是 maintenance 領域最清楚的結構:

  • views/maint/* 承接 route 與頁面流程協調
  • PageMaint.vue 承接維護頁共用頁面骨架
  • components/maint/* 承接維護頁專用元件
  • composables/maint/* 承接 CRUD 流程、表單狀態與 editable grid 狀態

EditableGrid.vue 是目前最接近薄 view 的 maintenance 頁面。

模式 3router meta -> App.vue -> layout

這一層已正式成立:

  • route 決定 layout 類型
  • App.vue 決定套用哪個 shell
  • layout 專注在骨架與共用框架 UI

這代表 layout 的責任邊界不應再回頭混入頁面內部流程。

命名規則

頁面與 page component

  • 直接被 route 載入的檔案放 views
  • 負責完整頁面畫面組裝的元件,檔名用 Page 前綴
  • page component 不放進 base

目前例子:

資料夾命名

  • 多字資料夾一律使用 kebab-case
  • 不新增 snake_casePascalCase 資料夾

目前例子:

  • sk-admin-layout
  • master-detail

domain component 命名

  • 與特定領域強綁定的元件,優先用領域意圖命名
  • 不要為了抽象而保留含糊的舊前綴
  • 若元件只在 maint 領域使用,就留在 components/maint

目前仍待整理的區域

高優先度

原因:

  • 這些頁面仍保留較多頁面內資料轉換與事件協調

中優先度

  • 清理 src/stores/stores/* 重複結構
  • 檢查 components/maint 內是否仍有可再明確命名的舊名稱
  • PageMaint 的後續演進,決定是否維持在 components 根目錄

中低優先度

  • 持續檢查 views 是否有可再下放到 page component 的模板片段
  • 清理命名調整後留下的空資料夾或死連結

新增或修改檔案時的判斷準則

  1. 這個檔案是否直接被 route 載入?
    • 是:優先放 views
  2. 這個檔案是否負責某個完整頁面的主畫面組裝?
    • 是:用 Page 前綴,放 page component 層,不要塞進 base
  3. 這段重複的是模板還是流程?
    • 模板:抽元件
    • 流程:抽 composable 或 store
  4. 這個狀態是否跨頁共享,或需要快取 / 全域顯示控制?
    • 是:優先考慮 store
  5. 這個邏輯是否在處理 API、token、session、錯誤正規化?
    • 是:放 services
  6. 這個元件是否只屬於單一 domain?
    • 是:優先放到該 domain 目錄,例如 components/maint
  7. 這個抽象是否真的降低重複與理解成本?
    • 否:不要抽