跳至主要内容

專案結構

本文檔說明基於 AppFuse Web 的專案結構,包含應用程式結構和框架內部結構。

應用程式結構

以下是使用 AppFuse Web 建構的應用程式推薦結構:

my-web-app/
├── public/ # 靜態資源
│ ├── favicon.ico
│ └── mockServiceWorker.js # MSW Service Worker
├── src/
│ ├── applets/ # 業務功能模組
│ ├── components/ # 共用組件
│ ├── layouts/ # 佈局組件
│ ├── features/ # Redux slices
│ ├── services/ # API 服務層
│ ├── mocks/ # MSW mock handlers
│ ├── hooks/ # 自訂 hooks
│ ├── utils/ # 工具函數
│ ├── config/ # 應用程式配置
│ ├── routes/ # 路由配置
│ ├── nls/ # 國際化資源
│ ├── types/ # TypeScript 類型定義
│ ├── App.tsx # 主應用程式組件
│ ├── main.tsx # 應用程式入口
│ └── tailwind.css # Tailwind CSS
├── index.html # HTML 模板
├── package.json # 專案配置
├── vite.config.ts # Vite 配置
├── tailwind.config.ts # Tailwind 配置
└── tsconfig.json # TypeScript 配置

核心目錄說明

applets/ - 業務功能模組

Applet 是獨立的業務功能模組,包含完整的 UI、狀態、邏輯:

applets/
├── products/ # 商品管理 Applet
│ ├── ProductListApplet.tsx
│ ├── ProductDetailApplet.tsx
│ └── types.ts
├── orders/ # 訂單管理 Applet
│ ├── OrderListApplet.tsx
│ └── OrderDetailApplet.tsx
└── settings/ # 設定 Applet
└── SettingsApplet.tsx

Applet 特性

  • 獨立的路由入口
  • 可包含子路由
  • 使用 <AppletShell> 提供標準佈局
  • 透過 Application Launcher 註冊

範例:參考 app-web/src/applets

components/ - 共用組件

存放專案內跨 Applet 共用的組件:

components/
├── PageHeader/ # 頁面標題組件
│ ├── PageHeader.tsx
│ └── PageHeader.test.tsx
├── DataCard/ # 資料卡片組件
│ └── DataCard.tsx
└── ErrorBoundary/ # 錯誤邊界組件
└── ErrorBoundary.tsx

使用時機

  • 框架元件庫未提供
  • 多個 Applet 需要使用
  • 專案特定的業務組件

原則

  • 優先使用 @appfuse/appfuse-web 的組件
  • 僅建立必要的自訂組件
  • 避免過度抽象

layouts/ - 佈局組件

定義應用程式的整體佈局:

layouts/
├── MainLayout.tsx # 主要佈局(含 Header、Sidebar)
├── AuthLayout.tsx # 認證頁面佈局
└── ErrorLayout.tsx # 錯誤頁面佈局

職責

  • 定義頁面框架
  • 整合導航元件
  • 處理佈局狀態(sidebar 展開/收合)

features/ - Redux Slices

使用 Redux Toolkit 管理全域狀態:

features/
├── auth/ # 認證狀態
│ ├── authSlice.ts
│ └── authThunks.ts
├── tenant/ # 租戶狀態
│ └── tenantSlice.ts
└── ui/ # UI 狀態(sidebar、theme)
└── uiSlice.ts

設計原則

  • 僅存放真正全域的狀態
  • 局部狀態使用 React state 或 URL state
  • 使用 RTK Query 處理 API 快取

services/ - API 服務層

封裝與後端的通訊:

services/
├── api.ts # API 客戶端配置
├── auth.service.ts # 認證相關 API
├── product.service.ts # 商品相關 API
└── order.service.ts # 訂單相關 API

職責

  • 定義 API 端點
  • 處理請求/回應轉換
  • 統一錯誤處理

範例

// services/product.service.ts
import { api } from './api';
import type { Product, ProductCreateRequest } from '@/types';

export const productService = {
findAll: () => api.get<Product[]>('/api/products'),

findById: (id: string) => api.get<Product>(`/api/products/${id}`),

create: (data: ProductCreateRequest) =>
api.post<Product>('/api/products', data),
};

mocks/ - MSW Mock Handlers

定義 Mock API 回應:

mocks/
├── browser.ts # MSW 瀏覽器設定
├── handlers/ # Mock handlers
│ ├── auth.handlers.ts
│ ├── product.handlers.ts
│ └── order.handlers.ts
└── data/ # Mock 資料
├── products.ts
└── orders.ts

使用方式

// mocks/handlers/product.handlers.ts
import { http, HttpResponse } from 'msw';
import { products } from '../data/products';

export const productHandlers = [
http.get('/api/products', () => {
return HttpResponse.json(products);
}),

http.post('/api/products', async ({ request }) => {
const newProduct = await request.json();
return HttpResponse.json(newProduct, { status: 201 });
}),
];

Mock → 真實 API 切換:參考 Mock API 指南

config/ - 應用程式配置

config/
├── app.config.ts # 應用程式配置
├── routes.config.ts # 路由配置
└── theme.config.ts # 主題配置

範例

// config/app.config.ts
export const appConfig = {
name: 'My Web App',
version: '0.0.1',
apiBaseUrl: import.meta.env.VITE_API_BASE_URL || 'http://localhost:8080',
enableMockAPI: import.meta.env.DEV,
};

routes/ - 路由配置

定義應用程式路由:

routes/
├── index.tsx # 主路由配置
├── AppRoutes.tsx # 應用程式路由
└── AuthRoutes.tsx # 認證路由

範例

// routes/AppRoutes.tsx
import { Routes, Route } from 'react-router-dom';
import { ProductListApplet } from '@/applets/products';

export function AppRoutes() {
return (
<Routes>
<Route path="/products" element={<ProductListApplet />} />
{/* 其他路由 */}
</Routes>
);
}

nls/ - 國際化資源

存放多語言資源檔:

nls/
├── zh-TW/ # 繁體中文
│ ├── common.json
│ ├── products.json
│ └── orders.json
└── en-US/ # 英文
├── common.json
├── products.json
└── orders.json

使用方式:參考 國際化指南

配置檔案

vite.config.ts

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';
import path from 'path';

export default defineConfig({
plugins: [react(), tailwindcss()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
server: {
port: 5173,
},
});

tailwind.config.ts

import type { Config } from 'tailwindcss';

export default {
content: [
'./index.html',
'./src/**/*.{js,ts,jsx,tsx}',
'./node_modules/@appfuse/appfuse-web/lib/**/*.{ts,tsx}',
],
theme: {
extend: {
// 自訂主題
},
},
plugins: [],
} satisfies Config;

src/tailwind.css

@import "tailwindcss";

/*
* 掃描元件庫的原始碼,讓 Tailwind 統一生成 CSS
*/
@source "../node_modules/@appfuse/appfuse-web/lib/**/*.{ts,tsx}";

/* 自訂樣式 */

最佳實踐

1. Import 路徑

使用路徑別名提高可讀性:

推薦

import { Button } from '@appfuse/appfuse-web';
import { ProductCard } from '@/components/ProductCard';
import { useAuth } from '@/hooks/useAuth';

避免

import { Button } from '../../../node_modules/@appfuse/appfuse-web';
import { ProductCard } from '../../../components/ProductCard';

2. 狀態管理策略

局部狀態 → React useState
URL 狀態 → useSearchParams
全域狀態 → Redux Toolkit
伺服器狀態 → RTK Query

3. 檔案命名

  • 組件:PascalCase.tsx(如 ProductCard.tsx
  • Hooks:camelCase.ts(如 useAuth.ts
  • 工具函數:camelCase.ts(如 formatDate.ts
  • 類型定義:types.ts*.types.ts

框架內部結構

以下是 @appfuse/appfuse-web 框架本身的結構,供進階參考:

appfuse-web/
├── lib/ # 框架層(發布為 @appfuse/appfuse-web)
│ ├── components/ # UI 元件庫(30+ 元件)
│ ├── form/ # 表單元件(react-hook-form 整合)
│ ├── utils/ # 工具函數(http, i18n, time 等)
│ ├── hooks/ # React Hooks
│ ├── messaging/ # 全域訊息系統(Toast + Dialog)
│ ├── main.ts # 框架進入點
│ └── tailwind.css # 基礎 Tailwind CSS

├── src/ # 原型層(參考實作)
│ ├── applets/ # 功能模組(product, order, customer 等)
│ ├── services/ # API 服務層
│ ├── components/ # 共用元件
│ ├── layouts/ # 版面配置
│ ├── routes/ # React Router 路由
│ ├── mocks/ # MSW Mock API
│ ├── features/ # Redux Slices
│ ├── store/ # Redux Store
│ ├── config/ # 應用程式配置
│ ├── types/ # TypeScript 型別
│ ├── nls/ # 國際化資源
│ └── assets/ # 靜態資源

├── tests/ # E2E 測試(Playwright)
├── docs/ # 框架文檔
└── .storybook/ # Storybook 配置

框架元件庫 (lib/components/)

按功能分類的 UI 元件:

分類路徑包含元件
資料輸入data-input/Input, Select, Checkbox, Radio, Switch, Textarea, TagInput, FileInput, MediaInput, RichTextEditor
資料顯示data-display/DataTable, VirtualTable, Icon, MediaViewer, AuthImage
動作actions/Button
圖表charts/LineChart, AreaChart, BarChart, PieChart, RadarChart, FunnelChart, ScatterPlot
版面layout/CollapsibleCard
導航navigation/Tabs
回饋feedback/Toast, ToastContainer, Dialog
覆蓋overlays/Dropdown

框架工具函數 (lib/utils/)

模組用途
httpHTTP 客戶端(axios 封裝,自動日期轉換)
filter查詢過濾器建構(RSQL/FIQL 標準)
browser瀏覽器偵測、語言偵測
cnCSS 類別合併(Tailwind CSS)
cookieCookie 管理
environ環境配置
i18n國際化
logger日誌系統
numeral數字格式化
template字串模板
time日期時間處理

框架匯入路徑

// 元件
import { Button, Input } from '@appfuse/appfuse-web/components';

// 表單元件
import { Input, Select } from '@appfuse/appfuse-web/form';

// 工具函數
import { createHttpClient } from '@appfuse/appfuse-web/utils';
import { time } from '@appfuse/appfuse-web/utils';

// Hooks
import { useTimeout } from '@appfuse/appfuse-web/hooks';

// 訊息系統
import { prompt } from '@appfuse/appfuse-web/messaging';

下一步

參考資源