前言
在大型 Angular 專案中使用 NgRx 進行狀態管理時,良好的目錄結構至關重要。這篇文章分享一個清晰的專案架構,幫助你建立更具模塊化和可維護性的程式碼。
Angular 專案架構建議
清晰地分離不同的模塊,使代碼更具模塊化和可維護性。
項目目錄結構
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
);
總結
這樣的架構有助於清晰地分離業務邏輯和狀態管理邏輯。雖然一開始可能感覺結構複雜,但當專案規模變大時,制定 SOP 有助於提高團隊的協作效率。
主要目錄功能:
core:存放全域的服務、守衛、攔截器等shared:存放共享的組件、管道、指令和服務features:每個業務模組內新增state目錄,存放 NgRx 的 actions、reducers、effects、selectors 等文件,用於狀態管理