@qlover/create-app 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/react-app/README.en.md +257 -0
- package/dist/templates/react-app/README.md +29 -231
- package/dist/templates/react-app/__tests__/__mocks__/I18nService.ts +13 -0
- package/dist/templates/react-app/__tests__/__mocks__/MockAppConfit.ts +48 -0
- package/dist/templates/react-app/__tests__/__mocks__/MockDialogHandler.ts +16 -0
- package/dist/templates/react-app/__tests__/__mocks__/MockLogger.ts +14 -0
- package/dist/templates/react-app/__tests__/__mocks__/createMockGlobals.ts +92 -0
- package/dist/templates/react-app/__tests__/setup/index.ts +51 -0
- package/dist/templates/react-app/__tests__/src/App.test.tsx +139 -0
- package/dist/templates/react-app/__tests__/src/base/cases/AppConfig.test.ts +288 -0
- package/dist/templates/react-app/__tests__/src/base/cases/AppError.test.ts +102 -0
- package/dist/templates/react-app/__tests__/src/base/cases/DialogHandler.test.ts +228 -0
- package/dist/templates/react-app/__tests__/src/base/cases/I18nKeyErrorPlugin.test.ts +207 -0
- package/dist/templates/react-app/__tests__/src/base/cases/InversifyContainer.test.ts +181 -0
- package/dist/templates/react-app/__tests__/src/base/cases/PublicAssetsPath.test.ts +61 -0
- package/dist/templates/react-app/__tests__/src/base/cases/RequestLogger.test.ts +199 -0
- package/dist/templates/react-app/__tests__/src/base/cases/RequestStatusCatcher.test.ts +192 -0
- package/dist/templates/react-app/__tests__/src/base/cases/RouterLoader.test.ts +235 -0
- package/dist/templates/react-app/__tests__/src/base/services/I18nService.test.ts +224 -0
- package/dist/templates/react-app/__tests__/src/core/IOC.test.ts +257 -0
- package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapsApp.test.ts +72 -0
- package/dist/templates/react-app/__tests__/src/main.integration.test.tsx +62 -0
- package/dist/templates/react-app/__tests__/src/main.test.tsx +46 -0
- package/dist/templates/react-app/__tests__/src/uikit/components/BaseHeader.test.tsx +88 -0
- package/dist/templates/react-app/config/app.router.ts +155 -0
- package/dist/templates/react-app/config/common.ts +9 -1
- package/dist/templates/react-app/docs/en/bootstrap.md +562 -0
- package/dist/templates/react-app/docs/en/development-guide.md +523 -0
- package/dist/templates/react-app/docs/en/env.md +482 -0
- package/dist/templates/react-app/docs/en/global.md +509 -0
- package/dist/templates/react-app/docs/en/i18n.md +268 -0
- package/dist/templates/react-app/docs/en/index.md +173 -0
- package/dist/templates/react-app/docs/en/ioc.md +424 -0
- package/dist/templates/react-app/docs/en/project-structure.md +434 -0
- package/dist/templates/react-app/docs/en/request.md +425 -0
- package/dist/templates/react-app/docs/en/router.md +404 -0
- package/dist/templates/react-app/docs/en/store.md +321 -0
- package/dist/templates/react-app/docs/en/test-guide.md +782 -0
- package/dist/templates/react-app/docs/en/theme.md +424 -0
- package/dist/templates/react-app/docs/en/typescript-guide.md +473 -0
- package/dist/templates/react-app/docs/zh/bootstrap.md +7 -0
- package/dist/templates/react-app/docs/zh/development-guide.md +523 -0
- package/dist/templates/react-app/docs/zh/env.md +24 -25
- package/dist/templates/react-app/docs/zh/global.md +28 -27
- package/dist/templates/react-app/docs/zh/i18n.md +268 -0
- package/dist/templates/react-app/docs/zh/index.md +173 -0
- package/dist/templates/react-app/docs/zh/ioc.md +44 -32
- package/dist/templates/react-app/docs/zh/project-structure.md +434 -0
- package/dist/templates/react-app/docs/zh/request.md +429 -0
- package/dist/templates/react-app/docs/zh/router.md +408 -0
- package/dist/templates/react-app/docs/zh/store.md +321 -0
- package/dist/templates/react-app/docs/zh/test-guide.md +782 -0
- package/dist/templates/react-app/docs/zh/theme.md +424 -0
- package/dist/templates/react-app/docs/zh/typescript-guide.md +473 -0
- package/dist/templates/react-app/package.json +9 -20
- package/dist/templates/react-app/src/base/cases/AppConfig.ts +16 -9
- package/dist/templates/react-app/src/base/cases/PublicAssetsPath.ts +7 -1
- package/dist/templates/react-app/src/base/services/I18nService.ts +15 -4
- package/dist/templates/react-app/src/base/services/RouteService.ts +43 -7
- package/dist/templates/react-app/src/core/bootstraps/BootstrapApp.ts +31 -10
- package/dist/templates/react-app/src/core/bootstraps/BootstrapsRegistry.ts +1 -1
- package/dist/templates/react-app/src/core/globals.ts +1 -3
- package/dist/templates/react-app/src/core/registers/RegisterCommon.ts +5 -3
- package/dist/templates/react-app/src/main.tsx +6 -1
- package/dist/templates/react-app/src/pages/404.tsx +0 -1
- package/dist/templates/react-app/src/pages/500.tsx +1 -1
- package/dist/templates/react-app/src/pages/base/RedirectPathname.tsx +3 -1
- package/dist/templates/react-app/src/styles/css/antd-themes/dark.css +3 -1
- package/dist/templates/react-app/src/styles/css/antd-themes/index.css +1 -1
- package/dist/templates/react-app/src/styles/css/antd-themes/pink.css +6 -1
- package/dist/templates/react-app/src/styles/css/page.css +1 -1
- package/dist/templates/react-app/src/uikit/components/BaseHeader.tsx +9 -2
- package/dist/templates/react-app/src/uikit/components/LocaleLink.tsx +5 -3
- package/dist/templates/react-app/src/uikit/hooks/useI18nGuard.ts +4 -6
- package/dist/templates/react-app/tsconfig.json +2 -1
- package/dist/templates/react-app/tsconfig.test.json +13 -0
- package/dist/templates/react-app/vite.config.ts +3 -2
- package/package.json +1 -1
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
# 开发规范指南
|
|
2
|
+
|
|
3
|
+
## 目录
|
|
4
|
+
|
|
5
|
+
1. [项目结构规范](#项目结构规范)
|
|
6
|
+
2. [代码风格规范](#代码风格规范)
|
|
7
|
+
3. [组件开发规范](#组件开发规范)
|
|
8
|
+
4. [状态管理规范](#状态管理规范)
|
|
9
|
+
5. [路由开发规范](#路由开发规范)
|
|
10
|
+
6. [国际化开发规范](#国际化开发规范)
|
|
11
|
+
7. [主题样式规范](#主题样式规范)
|
|
12
|
+
8. [测试规范](#测试规范)
|
|
13
|
+
9. [文档规范](#文档规范)
|
|
14
|
+
|
|
15
|
+
## 项目结构规范
|
|
16
|
+
|
|
17
|
+
> 💡 这里仅列出基本规范,完整的项目结构说明请参考 [项目结构文档](./project-structure.md)
|
|
18
|
+
|
|
19
|
+
### 1. 目录结构
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
src/
|
|
23
|
+
├── base/ # 基础功能实现
|
|
24
|
+
│ ├── cases/ # 业务场景实现
|
|
25
|
+
│ ├── services/ # 核心服务实现
|
|
26
|
+
│ └── types/ # 类型定义
|
|
27
|
+
├── core/ # 核心功能
|
|
28
|
+
│ ├── bootstraps/ # 启动相关
|
|
29
|
+
│ ├── registers/ # 注册器
|
|
30
|
+
│ └── IOC.ts # IOC 容器
|
|
31
|
+
├── pages/ # 页面组件
|
|
32
|
+
│ ├── auth/ # 认证相关页面
|
|
33
|
+
│ └── base/ # 基础页面
|
|
34
|
+
├── styles/ # 样式文件
|
|
35
|
+
│ └── css/
|
|
36
|
+
│ ├── themes/ # 主题相关
|
|
37
|
+
│ └── antd-themes/ # Ant Design 主题
|
|
38
|
+
├── uikit/ # UI 组件库
|
|
39
|
+
│ ├── components/ # 通用组件
|
|
40
|
+
│ ├── contexts/ # React Context
|
|
41
|
+
│ ├── hooks/ # 自定义 Hooks
|
|
42
|
+
│ └── providers/ # 提供者组件
|
|
43
|
+
└── App.tsx # 应用入口
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 2. 命名规范
|
|
47
|
+
|
|
48
|
+
- **文件命名**:
|
|
49
|
+
- 组件文件:`PascalCase.tsx`(如:`UserProfile.tsx`)
|
|
50
|
+
- 工具文件:`camelCase.ts`(如:`formatDate.ts`)
|
|
51
|
+
- 类型文件:`PascalCase.types.ts`(如:`User.types.ts`)
|
|
52
|
+
- 样式文件:`camelCase.css`(如:`buttonStyles.css`)
|
|
53
|
+
|
|
54
|
+
- **目录命名**:
|
|
55
|
+
- 全小写,使用连字符分隔(如:`user-profile/`)
|
|
56
|
+
- 功能模块使用单数形式(如:`auth/`,而不是 `auths/`)
|
|
57
|
+
|
|
58
|
+
## 代码风格规范
|
|
59
|
+
|
|
60
|
+
> 💡 这里仅列出基本规范,更多 TypeScript 和 React 开发规范请参考 [TypeScript 开发规范](./typescript-guide.md)
|
|
61
|
+
|
|
62
|
+
### 1. TypeScript 规范
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
// 使用 interface 定义对象类型
|
|
66
|
+
interface UserProfile {
|
|
67
|
+
id: string;
|
|
68
|
+
name: string;
|
|
69
|
+
age?: number; // 可选属性使用 ?
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 使用 type 定义联合类型或工具类型
|
|
73
|
+
type Theme = 'light' | 'dark' | 'pink';
|
|
74
|
+
type Nullable<T> = T | null;
|
|
75
|
+
|
|
76
|
+
// 使用 enum 定义常量枚举
|
|
77
|
+
enum UserRole {
|
|
78
|
+
ADMIN = 'ADMIN',
|
|
79
|
+
USER = 'USER',
|
|
80
|
+
GUEST = 'GUEST'
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 函数类型声明
|
|
84
|
+
function processUser(user: UserProfile): void {
|
|
85
|
+
// 实现
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 泛型使用有意义的名称
|
|
89
|
+
interface Repository<TEntity> {
|
|
90
|
+
find(id: string): Promise<TEntity>;
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 2. React 规范
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
// 函数组件使用 FC 类型
|
|
98
|
+
interface Props {
|
|
99
|
+
name: string;
|
|
100
|
+
age: number;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const UserCard: FC<Props> = ({ name, age }) => {
|
|
104
|
+
return (
|
|
105
|
+
<div>
|
|
106
|
+
<h3>{name}</h3>
|
|
107
|
+
<p>{age}</p>
|
|
108
|
+
</div>
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// Hooks 规范
|
|
113
|
+
const useUser = (userId: string) => {
|
|
114
|
+
const [user, setUser] = useState<UserProfile | null>(null);
|
|
115
|
+
const [loading, setLoading] = useState(false);
|
|
116
|
+
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
// 实现
|
|
119
|
+
}, [userId]);
|
|
120
|
+
|
|
121
|
+
return { user, loading };
|
|
122
|
+
};
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## 组件开发规范
|
|
126
|
+
|
|
127
|
+
> 💡 这里仅列出基本规范,完整的组件开发指南请参考 [组件开发指南](./component-guide.md)
|
|
128
|
+
|
|
129
|
+
### 1. 组件分类
|
|
130
|
+
|
|
131
|
+
- **页面组件**:放在 `pages/` 目录下
|
|
132
|
+
- **业务组件**:放在对应业务模块目录下
|
|
133
|
+
- **通用组件**:放在 `uikit/components/` 目录下
|
|
134
|
+
- **布局组件**:放在 `uikit/layouts/` 目录下
|
|
135
|
+
|
|
136
|
+
### 2. 组件实现
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
// 1. 导入顺序
|
|
140
|
+
import { FC, useEffect, useState } from 'react'; // React 相关
|
|
141
|
+
import { useTranslation } from 'react-i18next'; // 第三方库
|
|
142
|
+
import { UserService } from '@/services/user'; // 项目内部导入
|
|
143
|
+
import { Button } from './Button'; // 相对路径导入
|
|
144
|
+
|
|
145
|
+
// 2. 类型定义
|
|
146
|
+
interface Props {
|
|
147
|
+
userId: string;
|
|
148
|
+
onUpdate?: (user: User) => void;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// 3. 组件实现
|
|
152
|
+
export const UserProfile: FC<Props> = ({ userId, onUpdate }) => {
|
|
153
|
+
// 3.1 Hooks 声明
|
|
154
|
+
const { t } = useTranslation();
|
|
155
|
+
const [user, setUser] = useState<User | null>(null);
|
|
156
|
+
|
|
157
|
+
// 3.2 副作用
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
// 实现
|
|
160
|
+
}, [userId]);
|
|
161
|
+
|
|
162
|
+
// 3.3 事件处理
|
|
163
|
+
const handleUpdate = () => {
|
|
164
|
+
// 实现
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// 3.4 渲染方法
|
|
168
|
+
const renderHeader = () => {
|
|
169
|
+
return <h2>{user?.name}</h2>;
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// 3.5 返回 JSX
|
|
173
|
+
return (
|
|
174
|
+
<div>
|
|
175
|
+
{renderHeader()}
|
|
176
|
+
<Button onClick={handleUpdate}>{t('common.update')}</Button>
|
|
177
|
+
</div>
|
|
178
|
+
);
|
|
179
|
+
};
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## 状态管理规范
|
|
183
|
+
|
|
184
|
+
> 💡 这里仅列出基本规范,完整的状态管理指南请参考 [Store 开发指南](./store.md)
|
|
185
|
+
|
|
186
|
+
### 1. Store 实现
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
// 1. 状态接口定义
|
|
190
|
+
interface UserState extends StoreStateInterface {
|
|
191
|
+
currentUser: User | null;
|
|
192
|
+
loading: boolean;
|
|
193
|
+
error: string | null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// 2. Store 实现
|
|
197
|
+
@injectable()
|
|
198
|
+
export class UserStore extends StoreInterface<UserState> {
|
|
199
|
+
constructor() {
|
|
200
|
+
super(() => ({
|
|
201
|
+
currentUser: null,
|
|
202
|
+
loading: false,
|
|
203
|
+
error: null
|
|
204
|
+
}));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// 3. 选择器定义
|
|
208
|
+
selector = {
|
|
209
|
+
currentUser: (state: UserState) => state.currentUser,
|
|
210
|
+
loading: (state: UserState) => state.loading
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// 4. 操作方法
|
|
214
|
+
async fetchUser(id: string) {
|
|
215
|
+
try {
|
|
216
|
+
this.emit({ ...this.state, loading: true });
|
|
217
|
+
const user = await api.getUser(id);
|
|
218
|
+
this.emit({ ...this.state, currentUser: user, loading: false });
|
|
219
|
+
} catch (error) {
|
|
220
|
+
this.emit({
|
|
221
|
+
...this.state,
|
|
222
|
+
error: error.message,
|
|
223
|
+
loading: false
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### 2. Store 使用
|
|
231
|
+
|
|
232
|
+
```tsx
|
|
233
|
+
function UserProfile() {
|
|
234
|
+
const userStore = IOC(UserStore);
|
|
235
|
+
const user = useStore(userStore, userStore.selector.currentUser);
|
|
236
|
+
const loading = useStore(userStore, userStore.selector.loading);
|
|
237
|
+
|
|
238
|
+
return <div>{loading ? <Loading /> : <UserInfo user={user} />}</div>;
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## 路由开发规范
|
|
243
|
+
|
|
244
|
+
> 💡 这里仅列出基本规范,完整的路由开发指南请参考 [路由开发指南](./router.md)
|
|
245
|
+
|
|
246
|
+
### 1. 基本规范
|
|
247
|
+
|
|
248
|
+
- 路由配置集中管理在 `config/app.router.ts` 中
|
|
249
|
+
- 使用声明式路由配置
|
|
250
|
+
- 路由组件放置在 `pages` 目录下
|
|
251
|
+
- 支持路由级别的代码分割
|
|
252
|
+
- 路由配置包含元数据支持
|
|
253
|
+
|
|
254
|
+
### 2. 示例
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
// 路由配置示例
|
|
258
|
+
export const baseRoutes: RouteConfigValue[] = [
|
|
259
|
+
{
|
|
260
|
+
path: '/:lng',
|
|
261
|
+
element: 'base/Layout',
|
|
262
|
+
meta: {
|
|
263
|
+
category: 'main'
|
|
264
|
+
},
|
|
265
|
+
children: [
|
|
266
|
+
{
|
|
267
|
+
path: 'users',
|
|
268
|
+
element: 'users/UserList',
|
|
269
|
+
meta: {
|
|
270
|
+
title: i18nKeys.PAGE_USERS_TITLE,
|
|
271
|
+
auth: true
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
]
|
|
275
|
+
}
|
|
276
|
+
];
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
更多路由配置和使用示例,请参考 [路由开发指南](./router.md)。
|
|
280
|
+
|
|
281
|
+
## 国际化开发规范
|
|
282
|
+
|
|
283
|
+
> 💡 这里仅列出基本规范,完整的国际化开发指南请参考 [国际化开发指南](./i18n.md)
|
|
284
|
+
|
|
285
|
+
### 1. 基本规范
|
|
286
|
+
|
|
287
|
+
- 使用标识符常量管理翻译键
|
|
288
|
+
- 通过 TypeScript 注释生成翻译资源
|
|
289
|
+
- 支持多语言路由
|
|
290
|
+
- 集中管理翻译文件
|
|
291
|
+
|
|
292
|
+
### 2. 示例
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
/**
|
|
296
|
+
* @description User list page title
|
|
297
|
+
* @localZh 用户列表
|
|
298
|
+
* @localEn User List
|
|
299
|
+
*/
|
|
300
|
+
export const PAGE_USERS_TITLE = 'page.users.title';
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
更多国际化配置和使用示例,请参考 [国际化开发指南](./i18n.md)。
|
|
304
|
+
|
|
305
|
+
## 主题样式规范
|
|
306
|
+
|
|
307
|
+
> 💡 这里仅列出基本规范,完整的主题开发指南请参考 [主题开发指南](./theme.md)
|
|
308
|
+
|
|
309
|
+
### 1. 基本规范
|
|
310
|
+
|
|
311
|
+
- 使用 CSS 变量管理主题
|
|
312
|
+
- 遵循 Tailwind CSS 使用规范
|
|
313
|
+
- 组件样式模块化
|
|
314
|
+
- 支持多主题切换
|
|
315
|
+
|
|
316
|
+
### 2. 示例
|
|
317
|
+
|
|
318
|
+
```css
|
|
319
|
+
:root {
|
|
320
|
+
--color-brand: 37 99 235;
|
|
321
|
+
--text-primary: 15 23 42;
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
更多主题配置和使用示例,请参考 [主题开发指南](./theme.md)。
|
|
326
|
+
|
|
327
|
+
## 测试规范
|
|
328
|
+
|
|
329
|
+
> 💡 这里仅列出基本规范,完整的测试指南请参考 [测试开发指南](./testing.md)
|
|
330
|
+
|
|
331
|
+
### 1. 基本规范
|
|
332
|
+
|
|
333
|
+
- 单元测试覆盖核心逻辑
|
|
334
|
+
- 组件测试关注交互和渲染
|
|
335
|
+
- 使用 Jest 和 Testing Library
|
|
336
|
+
- 保持测试简单和可维护
|
|
337
|
+
|
|
338
|
+
### 2. 示例
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
describe('UserProfile', () => {
|
|
342
|
+
it('should render user info', () => {
|
|
343
|
+
const user = { id: '1', name: 'Test' };
|
|
344
|
+
render(<UserProfile user={user} />);
|
|
345
|
+
expect(screen.getByText(user.name)).toBeInTheDocument();
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
更多测试示例和最佳实践,请参考 [测试开发指南](./testing.md)。
|
|
351
|
+
|
|
352
|
+
## 文档规范
|
|
353
|
+
|
|
354
|
+
> 💡 这里仅列出基本规范,完整的文档编写指南请参考 [文档编写指南](./documentation.md)
|
|
355
|
+
|
|
356
|
+
### 1. 代码注释
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
/**
|
|
360
|
+
* 用户服务
|
|
361
|
+
*
|
|
362
|
+
* @description 处理用户相关的业务逻辑
|
|
363
|
+
* @example
|
|
364
|
+
* const userService = IOC(UserService);
|
|
365
|
+
* await userService.login(credentials);
|
|
366
|
+
*/
|
|
367
|
+
@injectable()
|
|
368
|
+
export class UserService {
|
|
369
|
+
/**
|
|
370
|
+
* 用户登录
|
|
371
|
+
*
|
|
372
|
+
* @param credentials - 登录凭证
|
|
373
|
+
* @returns 登录成功的用户信息
|
|
374
|
+
* @throws {AuthError} 认证失败时抛出
|
|
375
|
+
*/
|
|
376
|
+
async login(credentials: Credentials): Promise<User> {
|
|
377
|
+
// 实现
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### 2. 文档结构
|
|
383
|
+
|
|
384
|
+
- **README.md**:项目概述、安装说明、快速开始
|
|
385
|
+
- **docs/**:
|
|
386
|
+
- `zh/`:中文文档
|
|
387
|
+
- `en/`:英文文档
|
|
388
|
+
- 按功能模块组织文档文件
|
|
389
|
+
|
|
390
|
+
### 3. 文档格式
|
|
391
|
+
|
|
392
|
+
```markdown
|
|
393
|
+
# 模块名称
|
|
394
|
+
|
|
395
|
+
## 概述
|
|
396
|
+
|
|
397
|
+
简要说明模块的功能和用途。
|
|
398
|
+
|
|
399
|
+
## 使用方式
|
|
400
|
+
|
|
401
|
+
代码示例和使用说明。
|
|
402
|
+
|
|
403
|
+
## API 文档
|
|
404
|
+
|
|
405
|
+
详细的 API 说明。
|
|
406
|
+
|
|
407
|
+
## 最佳实践
|
|
408
|
+
|
|
409
|
+
使用建议和注意事项。
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
## Git 提交规范
|
|
413
|
+
|
|
414
|
+
> 💡 这里仅列出基本规范,完整的 Git 工作流程请参考 [Git 工作流指南](./git-workflow.md)
|
|
415
|
+
|
|
416
|
+
### 1. 提交消息格式
|
|
417
|
+
|
|
418
|
+
```
|
|
419
|
+
<type>(<scope>): <subject>
|
|
420
|
+
|
|
421
|
+
<body>
|
|
422
|
+
|
|
423
|
+
<footer>
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
- **type**:
|
|
427
|
+
- `feat`:新功能
|
|
428
|
+
- `fix`:修复
|
|
429
|
+
- `docs`:文档更新
|
|
430
|
+
- `style`:代码格式(不影响代码运行的变动)
|
|
431
|
+
- `refactor`:重构
|
|
432
|
+
- `test`:增加测试
|
|
433
|
+
- `chore`:构建过程或辅助工具的变动
|
|
434
|
+
|
|
435
|
+
- **scope**:影响范围(可选)
|
|
436
|
+
- **subject**:简短描述
|
|
437
|
+
- **body**:详细描述(可选)
|
|
438
|
+
- **footer**:不兼容变动、关闭 issue(可选)
|
|
439
|
+
|
|
440
|
+
### 2. 示例
|
|
441
|
+
|
|
442
|
+
```
|
|
443
|
+
feat(auth): 添加用户角色管理功能
|
|
444
|
+
|
|
445
|
+
- 添加角色创建和编辑界面
|
|
446
|
+
- 实现角色权限配置
|
|
447
|
+
- 添加角色分配功能
|
|
448
|
+
|
|
449
|
+
Closes #123
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
## 性能优化规范
|
|
453
|
+
|
|
454
|
+
> 💡 这里仅列出基本规范,完整的性能优化指南请参考 [性能优化指南](./performance.md)
|
|
455
|
+
|
|
456
|
+
### 1. 代码分割
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
// 路由级别的代码分割
|
|
460
|
+
const UserModule = lazy(() => import('./pages/users'));
|
|
461
|
+
|
|
462
|
+
// 组件级别的代码分割
|
|
463
|
+
const HeavyComponent = lazy(() => import('./components/Heavy'));
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
### 2. 性能考虑
|
|
467
|
+
|
|
468
|
+
```typescript
|
|
469
|
+
// 使用 useMemo 缓存计算结果
|
|
470
|
+
const sortedUsers = useMemo(() => {
|
|
471
|
+
return users.sort((a, b) => a.name.localeCompare(b.name));
|
|
472
|
+
}, [users]);
|
|
473
|
+
|
|
474
|
+
// 使用 useCallback 缓存函数
|
|
475
|
+
const handleUpdate = useCallback(() => {
|
|
476
|
+
// 实现
|
|
477
|
+
}, [dependencies]);
|
|
478
|
+
|
|
479
|
+
// 使用 React.memo 避免不必要的重渲染
|
|
480
|
+
const UserCard = React.memo(({ user }) => {
|
|
481
|
+
return <div>{user.name}</div>;
|
|
482
|
+
});
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
## 安全规范
|
|
486
|
+
|
|
487
|
+
> 💡 这里仅列出基本规范,完整的安全开发指南请参考 [安全开发指南](./security.md)
|
|
488
|
+
|
|
489
|
+
### 1. 数据处理
|
|
490
|
+
|
|
491
|
+
```typescript
|
|
492
|
+
// 敏感数据加密
|
|
493
|
+
const encryptedData = encrypt(sensitiveData);
|
|
494
|
+
|
|
495
|
+
// XSS 防护
|
|
496
|
+
const sanitizedHtml = sanitizeHtml(userInput);
|
|
497
|
+
|
|
498
|
+
// CSRF 防护
|
|
499
|
+
api.defaults.headers['X-CSRF-Token'] = getCsrfToken();
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### 2. 权限控制
|
|
503
|
+
|
|
504
|
+
```typescript
|
|
505
|
+
// 路由权限
|
|
506
|
+
const PrivateRoute: FC = ({ children }) => {
|
|
507
|
+
const auth = useAuth();
|
|
508
|
+
return auth.isAuthenticated ? children : <Navigate to="/login" />;
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
// 操作权限
|
|
512
|
+
function AdminPanel() {
|
|
513
|
+
const { hasPermission } = useAuth();
|
|
514
|
+
|
|
515
|
+
return (
|
|
516
|
+
<div>
|
|
517
|
+
{hasPermission('ADMIN') && (
|
|
518
|
+
<button>管理员操作</button>
|
|
519
|
+
)}
|
|
520
|
+
</div>
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
```
|
|
@@ -48,12 +48,13 @@ Vite 会按照以下优先级加载环境变量文件:
|
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
**示例**:
|
|
51
|
+
|
|
51
52
|
```bash
|
|
52
53
|
# 开发模式
|
|
53
54
|
vite dev --mode development
|
|
54
55
|
# 加载顺序:.env.local > .env.development > .env
|
|
55
56
|
|
|
56
|
-
# 生产模式
|
|
57
|
+
# 生产模式
|
|
57
58
|
vite build --mode production
|
|
58
59
|
# 加载顺序:.env.local > .env.production > .env
|
|
59
60
|
|
|
@@ -94,15 +95,15 @@ import envConfig from '@qlover/corekit-bridge/vite-env-config';
|
|
|
94
95
|
export default defineConfig({
|
|
95
96
|
plugins: [
|
|
96
97
|
envConfig({
|
|
97
|
-
envPops: true,
|
|
98
|
-
envPrefix: 'VITE_',
|
|
98
|
+
envPops: true, // 启用环境变量加载
|
|
99
|
+
envPrefix: 'VITE_', // 环境变量前缀
|
|
99
100
|
records: [
|
|
100
|
-
['APP_NAME', name],
|
|
101
|
-
['APP_VERSION', version]
|
|
101
|
+
['APP_NAME', name], // 注入应用名称
|
|
102
|
+
['APP_VERSION', version] // 注入应用版本
|
|
102
103
|
]
|
|
103
104
|
})
|
|
104
105
|
],
|
|
105
|
-
envPrefix: 'VITE_',
|
|
106
|
+
envPrefix: 'VITE_', // Vite 环境变量前缀
|
|
106
107
|
server: {
|
|
107
108
|
port: Number(process.env.VITE_SERVER_PORT || 3200)
|
|
108
109
|
}
|
|
@@ -166,13 +167,13 @@ const bootstrap = new Bootstrap({
|
|
|
166
167
|
register: new IocRegisterImpl({ pathname, appConfig })
|
|
167
168
|
},
|
|
168
169
|
envOptions: {
|
|
169
|
-
target: appConfig,
|
|
170
|
+
target: appConfig, // 注入目标
|
|
170
171
|
source: {
|
|
171
|
-
...import.meta.env,
|
|
172
|
+
...import.meta.env, // 环境变量源
|
|
172
173
|
[envPrefix + 'BOOT_HREF']: root.location.href
|
|
173
174
|
},
|
|
174
|
-
prefix: envPrefix,
|
|
175
|
-
blackList: envBlackList
|
|
175
|
+
prefix: envPrefix, // 环境变量前缀
|
|
176
|
+
blackList: envBlackList // 黑名单(不注入的变量)
|
|
176
177
|
}
|
|
177
178
|
});
|
|
178
179
|
```
|
|
@@ -186,7 +187,7 @@ const bootstrap = new Bootstrap({
|
|
|
186
187
|
{
|
|
187
188
|
"scripts": {
|
|
188
189
|
"dev": "vite --mode localhost",
|
|
189
|
-
"dev:staging": "vite --mode staging",
|
|
190
|
+
"dev:staging": "vite --mode staging",
|
|
190
191
|
"dev:prod": "vite --mode production"
|
|
191
192
|
}
|
|
192
193
|
}
|
|
@@ -238,7 +239,6 @@ VITE_CUSTOM_FEATURE=true
|
|
|
238
239
|
function App() {
|
|
239
240
|
const apiUrl = import.meta.env.VITE_API_BASE_URL;
|
|
240
241
|
const isDebug = import.meta.env.VITE_DEBUG === 'true';
|
|
241
|
-
|
|
242
242
|
return (
|
|
243
243
|
<div>
|
|
244
244
|
<p>API URL: {apiUrl}</p>
|
|
@@ -254,14 +254,14 @@ function App() {
|
|
|
254
254
|
// 通过 IOC 获取配置
|
|
255
255
|
function UserService() {
|
|
256
256
|
const appConfig = IOC(IOCIdentifier.AppConfig);
|
|
257
|
-
|
|
257
|
+
|
|
258
258
|
const apiUrl = appConfig.aiApiBaseUrl;
|
|
259
259
|
const token = appConfig.aiApiToken;
|
|
260
|
-
|
|
260
|
+
|
|
261
261
|
// 使用配置进行 API 调用
|
|
262
262
|
const response = await fetch(`${apiUrl}/chat`, {
|
|
263
263
|
headers: {
|
|
264
|
-
|
|
264
|
+
Authorization: `Bearer ${token}`
|
|
265
265
|
}
|
|
266
266
|
});
|
|
267
267
|
}
|
|
@@ -272,17 +272,15 @@ function UserService() {
|
|
|
272
272
|
```tsx
|
|
273
273
|
@injectable()
|
|
274
274
|
export class ApiService {
|
|
275
|
-
constructor(
|
|
276
|
-
@inject(IOCIdentifier.AppConfig) private appConfig: AppConfig
|
|
277
|
-
) {}
|
|
275
|
+
constructor(@inject(IOCIdentifier.AppConfig) private appConfig: AppConfig) {}
|
|
278
276
|
|
|
279
277
|
async makeRequest() {
|
|
280
278
|
const baseUrl = this.appConfig.aiApiBaseUrl;
|
|
281
279
|
const token = this.appConfig.aiApiToken;
|
|
282
|
-
|
|
280
|
+
|
|
283
281
|
return fetch(`${baseUrl}/api/endpoint`, {
|
|
284
282
|
headers: {
|
|
285
|
-
|
|
283
|
+
Authorization: `Bearer ${token}`
|
|
286
284
|
}
|
|
287
285
|
});
|
|
288
286
|
}
|
|
@@ -302,7 +300,6 @@ export class InjectEnv implements BootstrapExecutorPlugin {
|
|
|
302
300
|
|
|
303
301
|
onBefore(): void {
|
|
304
302
|
const { target, source, prefix, blackList } = this.options;
|
|
305
|
-
|
|
306
303
|
// 遍历目标对象的属性
|
|
307
304
|
for (const key in target) {
|
|
308
305
|
if (blackList.includes(key)) {
|
|
@@ -326,13 +323,13 @@ export class InjectEnv implements BootstrapExecutorPlugin {
|
|
|
326
323
|
```tsx
|
|
327
324
|
env<D>(key: string, defaultValue?: D): D {
|
|
328
325
|
const { prefix = '', source = {} } = this.options;
|
|
329
|
-
|
|
326
|
+
|
|
330
327
|
// 将驼峰命名转换为下划线命名
|
|
331
328
|
const formattedKey = key.replace(/([a-z])([A-Z])/g, '$1_$2').toUpperCase();
|
|
332
329
|
const envKey = `${prefix}${formattedKey}`;
|
|
333
|
-
|
|
330
|
+
|
|
334
331
|
const value = source[envKey];
|
|
335
|
-
|
|
332
|
+
|
|
336
333
|
// 如果是 JSON 字符串,则解析
|
|
337
334
|
if (typeof value === 'string' && InjectEnv.isJSONString(value)) {
|
|
338
335
|
return JSON.parse(value);
|
|
@@ -403,7 +400,6 @@ export class AppConfig implements EnvConfigInterface {
|
|
|
403
400
|
|
|
404
401
|
private validateRequiredEnvVars(): void {
|
|
405
402
|
const required = ['VITE_API_BASE_URL', 'VITE_APP_NAME'];
|
|
406
|
-
|
|
407
403
|
for (const envVar of required) {
|
|
408
404
|
if (!import.meta.env[envVar]) {
|
|
409
405
|
throw new Error(`Missing required environment variable: ${envVar}`);
|
|
@@ -426,6 +422,7 @@ console.log('AppConfig:', IOC(IOCIdentifier.AppConfig));
|
|
|
426
422
|
### 2. 常见问题
|
|
427
423
|
|
|
428
424
|
**问题 1:环境变量未注入**
|
|
425
|
+
|
|
429
426
|
```bash
|
|
430
427
|
# 检查环境变量前缀
|
|
431
428
|
# 确保使用 VITE_ 前缀
|
|
@@ -434,6 +431,7 @@ APP_NAME=MyApp # ❌ 错误,不会被注入
|
|
|
434
431
|
```
|
|
435
432
|
|
|
436
433
|
**问题 2:环境变量文件未加载**
|
|
434
|
+
|
|
437
435
|
```bash
|
|
438
436
|
# 检查文件命名
|
|
439
437
|
.env.development # ✅ 正确
|
|
@@ -441,6 +439,7 @@ APP_NAME=MyApp # ❌ 错误,不会被注入
|
|
|
441
439
|
```
|
|
442
440
|
|
|
443
441
|
**问题 3:环境变量被黑名单过滤**
|
|
442
|
+
|
|
444
443
|
```tsx
|
|
445
444
|
// 检查黑名单配置
|
|
446
445
|
export const envBlackList = ['env', 'userNodeEnv'];
|