Angular 專案架構建議
在專案中使用 NgRx 進行狀態管理,
並且清晰地分離不同的模塊,
使代碼更具模塊化和可維護性
項目目錄結構
src/
│
├── app/
│ ├── core/
│ │ ├── guards/ # 守衛
│ │ ├── interceptors/ # 攔截器
│ │ ├── services/ # 全域服務
│ │ ├── models/ # 全域模型
│ │ ├── config/ # 配置
│ │ ├── utils/ # 工具函數
│ │ ├── core.module.ts # 核心模組
│ │ └── index.ts # 導出核心模組內容
│ ├── shared/
│ │ ├── components/ # 共享組件
│ │ ├── pipes/ # 共享管道
│ │ ├── directives/ # 共享指令
│ │ ├── services/ # 共享服務
│ │ ├── shared.module.ts # 共享模組
│ │ └── index.ts # 導出共享模組內容
│ ├── features/
│ │ ├── auth/ # 認證模組
│ │ │ ├── login/ # 登入功能
│ │ │ ├── register/ # 註冊功能
│ │ │ ├── state/ # NgRx狀態管理
│ │ │ │ ├── actions/ # Actions
│ │ │ │ ├── effects/ # Effects
│ │ │ │ ├── reducers/ # Reducers
│ │ │ │ ├── selectors/ # Selectors
│ │ │ │ └── index.ts # 導出NgRx狀態管理內容
│ │ │ ├── auth.service.ts # 認證服務
│ │ │ ├── auth.guard.ts # 認證守衛
│ │ │ ├── auth.module.ts # 認證模組
│ │ │ └── auth-routing.module.ts # 認證模組路由
│ │ ├── user/ # 用戶管理模組
│ │ │ ├── components/ # 用戶管理相關組件
│ │ │ ├── services/ # 用戶管理相關服務
│ │ │ ├── models/ # 用戶管理相關模型
│ │ │ ├── state/ # NgRx狀態管理
│ │ │ │ ├── actions/ # Actions
│ │ │ │ ├── effects/ # Effects
│ │ │ │ ├── reducers/ # Reducers
│ │ │ │ ├── selectors/ # Selectors
│ │ │ │ └── index.ts # 導出NgRx狀態管理內容
│ │ │ ├── user.module.ts # 用戶管理模組
│ │ │ └── user-routing.module.ts # 用戶管理模組路由
│ │ ├── product/ # 商品管理模組
│ │ │ ├── components/ # 商品管理相關組件
│ │ │ ├── services/ # 商品管理相關服務
│ │ │ ├── models/ # 商品管理相關模型
│ │ │ ├── state/ # NgRx狀態管理
│ │ │ │ ├── actions/ # Actions
│ │ │ │ ├── effects/ # Effects
│ │ │ │ ├── reducers/ # Reducers
│ │ │ │ ├── selectors/ # Selectors
│ │ │ │ └── index.ts # 導出NgRx狀態管理內容
│ │ │ ├── product.module.ts # 商品管理模組
│ │ │ └── product-routing.module.ts # 商品管理模組路由
│ │ └── ... # 其他功能模組
│ ├── app-routing.module.ts
│ ├── app.module.ts
│ ├── app.component.ts
│ └── ...
│
├── assets/
│
├── environments/
│
├── styles/
│
├── index.html
│
├── main.ts
│
└── polyfills.ts
core
目錄
可以用來存放 全域 的:
服務(service)
守衛(guard)
攔截器(interceptor)
模型(model)
配置(config)
工具函數(utils)
等等
shared
目錄
可以用來存放 共用、共享 的:
組件(components)
管道(pipes)
指令(directives)
服務(services)
等等
features
目錄
每個業務模組內部包含一個 state
目錄,
用於存放 NgRx 的狀態管理相關文件
NgRx 狀態管理目錄結構
state
目錄
├── state/
│ ├── actions/ # Actions
│ │ └── *.actions.ts
│ ├── effects/ # Effects
│ │ └── *.effects.ts
│ ├── reducers/ # Reducers
│ │ └── *.reducer.ts
│ ├── selectors/ # Selectors
│ │ └── *.selectors.ts
│ └── index.ts # 導出 NgRx 狀態管理內容
state
目錄詳細內容
actions/: 存放所有的 Actions, 用於描述對狀態進行的操作
*.actions.ts
: 定義 Actions 類型和 Actions 創建函數
effects/: 存放所有的 Effects, 用於處理副作用(如異步操作)
*.effects.ts
: 定義 Effects 類型和 Effects 處理函數
reducers/: 存放所有的 Reducers, 用於處理 Actions 並更新狀態
*.reducer.ts
: 定義 Reducers 類型和 Reducers 處理函數
selectors/: 存放所有的 Selectors, 用於從狀態中選擇數據
*.selectors.ts
: 定義 Selectors 函數
index.ts: 導出
state
目錄中的內容, 以便在模組中導入使用
actions/auth.actions.ts
import { createAction, props } from "@ngrx/store";
export const login = createAction(
"[Auth] Login",
props<{ username: string; password: string }>()
);
export const loginSuccess = createAction(
"[Auth] Login Success",
props<{ user: any }>()
);
export const loginFailure = createAction(
"[Auth] Login Failure",
props<{ error: any }>()
);
effects/auth.effects.ts
import { Injectable } from "@angular/core";
import { Actions, createEffect, ofType } from "@ngrx/effects";
import { AuthService } from "../auth.service";
import { login, loginSuccess, loginFailure } from "./auth.actions";
import { catchError, map, switchMap } from "rxjs/operators";
import { of } from "rxjs";
@Injectable()
export class AuthEffects {
login$ = createEffect(() =>
this.actions$.pipe(
ofType(login),
switchMap((action) =>
this.authService.login(action.username, action.password).pipe(
map((user) => loginSuccess({ user })),
catchError((error) => of(loginFailure({ error })))
)
)
)
);
constructor(private actions$: Actions, private authService: AuthService) {}
}
reducers/auth.reducer.ts
import { createReducer, on } from "@ngrx/store";
import { login, loginSuccess, loginFailure } from "./auth.actions";
export const initialState = {
user: null,
error: null,
loading: false,
};
const _authReducer = createReducer(
initialState,
on(login, (state) => ({ ...state, loading: true })),
on(loginSuccess, (state, { user }) => ({ ...state, user, loading: false })),
on(loginFailure, (state, { error }) => ({ ...state, error, loading: false }))
);
export function authReducer(state: any, action: any) {
return _authReducer(state, action);
}
selectors/auth.selectors.ts
import { createFeatureSelector, createSelector } from "@ngrx/store";
export const selectAuthState = createFeatureSelector<any>("auth");
export const selectUser = createSelector(
selectAuthState,
(state: any) => state.user
);
export const selectAuthError = createSelector(
selectAuthState,
(state: any) => state.error
);
export const selectAuthLoading = createSelector(
selectAuthState,
(state: any) => state.loading
);
總結
core
: 存放全域的服務、守衛、攔截器等shared
: 存放共享的組件、管道、指令和服務features
: 每個業務模組內新增state
目錄, 存放 NgRx 的 actions、reducers、effects、selectors 等文件, 用於狀態管理
這樣的架構有助於清晰地分離業務邏輯和狀態管理邏輯,
雖然感覺有點多餘,
但當專案變得大的時候,
制定 SOP 有助於提高團隊的協作效率
參考
Ngrx 官方網站
練習專案