JavaScript Structural Pattern For Project 開箱 JavaScript 設計模式 (2-3)

me
林彥成
2023-05-30 | 3 min.
文章目錄
  1. 1. Model View Controller Pattern (MVC)
  2. 2. Model-View-ViewModel Pattern (MVVM)
  3. 3. Flux Pattern

這篇文章接下來會讓優化從開資料夾開始,從優化專案結構的三種設計模式切入

  • Model View Controller Pattern (MVC)
  • Model-View-ViewModel Pattern (MVVM)
  • Flux Pattern

Design Patterns 依照目的分成三群:

Model View Controller Pattern (MVC)

Model View Controller (MVC) 模式是一種用於管理程式的結構和邏輯,提供定義清晰的架構,開發上能夠更容易地理解、擴展和測試,架構主要分為三個部分:

  • Model: 處理數據的讀取、寫入和驗證等業務邏輯操作
  • View: 處理數據的顯示和呈現以及用戶互動,可以是 HTML 樣板、頁面元素
  • Controller: 接收來自界面的輸入和事件,並根據這些輸入和事件來更新模型和 UI

MVC 是將應用程式的邏輯、數據和界面分離,達到結構化和可重用性。

  1. 第一種是依照資料表去做區分,把同個資料表的 model、controller、route 集中存放,覺得較適合大型專案。
1
2
3
4
5
6
7
8
9
10
11
├── src/
│ ├── entities/ # 按照資料表去區分
│ │ └── user/ # 使用者表
│ │ ├── model.js # Schema 定義
│ │ ├── controller.js # 資料庫 CRUD
│ │ └── route.js # API 設定
│ └── index.js # 入口
├── .eslintrc # ESLint 設定檔
├── package-lock.json
├── package.json
└── README.md
  1. 第二種則是依照功能性,controller、routes、models 集中放置在各自的分類資料夾,覺得較適合小型專案。
1
2
3
4
5
6
7
8
9
├── src/
│ ├── controller/ # 資料庫 CRUD
│ ├── routes/ # API 設定
│ ├── models/ # Schema 定義
│ └── index.js # 程式入口
├── .eslintrc # ESLint 設定檔
├── package-lock.json
├── package.json
└── README.md

Model-View-ViewModel Pattern (MVVM)

Model-View-ViewModel Pattern (MVVM) 著重在開發的關注點分離並提供單向或是雙向的 Data Binding 來控制 UI 的顯示邏輯。

  • Model: 定義資料結構,執行資料操作,在 React 中通常是元件中或是 Store 裡的狀態
  • View (stateless): UI 介面,根據 ViewModel 來顯示,實際上的 HTML element 由 JSX 背後的 virtual dom 所控制
  • ViewModel (stateful): 處理 Model 和 View 之間的溝通,用來維護 UI 的狀態,在 React 中的 JSX 就是扮演這個腳色

一個常見的 MVVM 資料流:

  1. ViewModel 從 Model 擷取資料並準備在 View 顯示
  2. View 把和 ViewModel binding 的資料顯示在 UI 中
  3. 透過 View 觸發命令或輸入事件
  4. ViewModel 監控互動並執行資料操作並更新 Model
  5. 如果 Model 資料變更,ViewModel 會通知視圖讓 UI 重新渲染

Flux Pattern

Flux pattern 是由 Facebook 提出將狀態中央化管理的機制,定義了單向資料流的架構明確將程式分為四個核心概念:

  • Actions: 描述事件,例如使用者操作或 API 請求完成,由 type 和 payload 組成
  • Dispatcher: 提供中央管理的機制,用於接收和發送 Actions,當事件發生時,Actions 會被集中到 Dispatcher 並操作對應的 Store
  • Stores: 負責存儲和管理應用程式的狀態,只接受 Dispatcher 過來的 Actions,並根據 Action Type 來更新狀態
  • Views: UI 訂閱 Store 的變化,並依據狀態來更新。當用戶進行操作時,UI 會產生 Action 並送到 Dispatcher 中去跟 Store 互動

搭配 Redux 後,主要就是增加元件之間需要溝通工具,會有以下特性:

  • 單向資料流
  • 公共的狀態儲存 (Store)

專案架構也會受到這種概念去分類:

  • actions: 觸發狀態改變用的 function
  • pages: 頁面容器元件
  • containers: 有連接 Store
  • components: 沒有連接 Store
  • reducers: 收到 action 後的資料邏輯

所以專案資料夾架構依照屬性去分類就會長成下面這個樣子,在完成任務的過程中需要在多個不同的資料夾中來回,覺得較適合小型專案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
└── src
├── actions
│ ├── typeOneActions.js
│ └── typeTwoActions.js
├── api
│ ├── apiHandler.js
│ ├── typeOneApi.js
│ └── typeTwoApi.js
├── components
│ ├── TypeOneComponent.jsx
│ ├── TypeOneListComponent.jsx
│ ├── TypeTwoComponent.jsx
│ ├── TypeTwoPageComponent.jsx
│ └── HomePageComponent.jsx
├── containers
│ ├── TypeOneContainer.js
│ └── TypeTwoPageContainer.js
├── pages
│ └── HomePageContainer.js
├── index.js
├── reducers
│ ├── typeOneReducer.js
│ └── typeTwoReducer.js
├── routes.js
├── store.js
└── utils

之前弄的小專案當例子,如果有加上 redux 的話,專案架構大概會長成下面的樣子,在比較簡單的專案中可以更簡化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
├── .storybook
├── src/
│ ├── constants/
│ ├── utils/
│ ├── pages/
│ ├── containers/ # containers 放置與 Redux 連接的相關元件,單元測試檔案為 `元件名稱.test.js`
│ ├── components/ # components 放置相關元件,單元測試檔案為 `元件名稱.test.js`
│ │ └── Root.js # 路由根目錄
│ ├── hooks/ # hooks 相關
│ ├── middleware/ # 資料處理相關
│ │ ├── API.js # axios 的 instance
│ │ └── redux-api.js # redux-api
│ ├── styles/ # 樣式檔們
│ ├── index.js # 程式入口
│ ├── serviceWorker.js
│ └── setupTests.js # 測試相關設定
├── .eslintrc # ESLint 設定檔
├── .prettierrc # prettierrc 工具設定
├── .travis.yml # 持續發佈工具
├── package-lock.json
├── package.json
└── README.md

在使用 Redux 的時候,我們通常要同時維護 reducer 以及 action,如果還要搭配 API 進行,又會需要多維護一個 middleware。

可是在簡單的專案中,絕大多數時候我們進行的工作都是類似的,官方也有相關的建議,甚至後來還推出了 toolkit 來協助寫法簡化。

簡單來說,本來要維護三個地方,現在變成只要維護一份配置檔就好了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
src
└── features
├── typeOne
│ ├── TypeOne.js
│ ├── TypeOne.styles.scss
│ └── typeOneSlice.js
├── typeTwo
│ ├── TypeTwo.js
│ ├── TypeTwo.styles.scss
│ └── typeTwoSlice.js
└── typeThree
├── TypeThree.js
├── TypeThree.styles.scss
└── typeThreeSlice.js

喜歡這篇文章,請幫忙拍拍手喔 🤣


share