@qlover/create-app 0.7.14 → 0.7.15
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 +23 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/next-app/README.en.md +131 -0
- package/dist/templates/next-app/README.md +115 -20
- package/dist/templates/next-app/docs/en/api.md +387 -0
- package/dist/templates/next-app/docs/en/component.md +544 -0
- package/dist/templates/next-app/docs/en/database.md +496 -0
- package/dist/templates/next-app/docs/en/development-guide.md +727 -0
- package/dist/templates/next-app/docs/en/env.md +563 -0
- package/dist/templates/next-app/docs/en/i18n.md +287 -0
- package/dist/templates/next-app/docs/en/index.md +166 -0
- package/dist/templates/next-app/docs/en/page.md +457 -0
- package/dist/templates/next-app/docs/en/project-structure.md +177 -0
- package/dist/templates/next-app/docs/en/router.md +427 -0
- package/dist/templates/next-app/docs/en/theme.md +532 -0
- package/dist/templates/next-app/docs/en/validator.md +478 -0
- package/dist/templates/next-app/docs/zh/api.md +387 -0
- package/dist/templates/next-app/docs/zh/component.md +544 -0
- package/dist/templates/next-app/docs/zh/database.md +496 -0
- package/dist/templates/next-app/docs/zh/development-guide.md +727 -0
- package/dist/templates/next-app/docs/zh/env.md +563 -0
- package/dist/templates/next-app/docs/zh/i18n.md +287 -0
- package/dist/templates/next-app/docs/zh/index.md +166 -0
- package/dist/templates/next-app/docs/zh/page.md +457 -0
- package/dist/templates/next-app/docs/zh/project-structure.md +177 -0
- package/dist/templates/next-app/docs/zh/router.md +427 -0
- package/dist/templates/next-app/docs/zh/theme.md +532 -0
- package/dist/templates/next-app/docs/zh/validator.md +476 -0
- package/package.json +1 -1
- package/dist/templates/next-app/docs/env.md +0 -94
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
# 组件开发和状态管理指南
|
|
2
|
+
|
|
3
|
+
## 目录
|
|
4
|
+
|
|
5
|
+
1. [组件架构概述](#组件架构概述)
|
|
6
|
+
2. [组件架构和设计原则](#组件架构和设计原则)
|
|
7
|
+
3. [状态管理系统](#状态管理系统)
|
|
8
|
+
4. [组件通信和事件处理](#组件通信和事件处理)
|
|
9
|
+
5. [组件测试和性能优化](#组件测试和性能优化)
|
|
10
|
+
6. [最佳实践和示例](#最佳实践和示例)
|
|
11
|
+
|
|
12
|
+
## 组件架构概述
|
|
13
|
+
|
|
14
|
+
### 1. 整体架构
|
|
15
|
+
|
|
16
|
+
项目采用分层的组件架构设计:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
组件层 状态层
|
|
20
|
+
┌──────────────┐ ┌──────────────┐
|
|
21
|
+
│ UI 组件 │ │ 状态接口 │
|
|
22
|
+
├──────────────┤ ├──────────────┤
|
|
23
|
+
│ 容器组件 │ ◄─────┤ 状态实现 │
|
|
24
|
+
├──────────────┤ ├──────────────┤
|
|
25
|
+
│ 业务组件 │ │ 状态动作 │
|
|
26
|
+
└──────────────┘ └──────────────┘
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 2. 核心概念
|
|
30
|
+
|
|
31
|
+
- **UI 组件**:纯展示组件,不包含业务逻辑
|
|
32
|
+
- **容器组件**:负责状态管理和业务逻辑
|
|
33
|
+
- **业务组件**:特定业务场景的组件
|
|
34
|
+
- **状态管理**:基于 Store 模式的状态管理系统
|
|
35
|
+
|
|
36
|
+
### 3. 技术栈
|
|
37
|
+
|
|
38
|
+
- **React + Next.js**:基础框架
|
|
39
|
+
- **TypeScript**:类型系统
|
|
40
|
+
- **Inversify**:依赖注入
|
|
41
|
+
- **Ant Design**:UI 组件库
|
|
42
|
+
- **Tailwind CSS**:样式系统
|
|
43
|
+
|
|
44
|
+
## 组件架构和设计原则
|
|
45
|
+
|
|
46
|
+
### 1. 组件分类
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
// 1. UI 组件
|
|
50
|
+
export function Button({ onClick, children }: ButtonProps) {
|
|
51
|
+
return (
|
|
52
|
+
<button
|
|
53
|
+
onClick={onClick}
|
|
54
|
+
className="px-4 py-2 bg-primary text-white rounded"
|
|
55
|
+
>
|
|
56
|
+
{children}
|
|
57
|
+
</button>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 2. 容器组件
|
|
62
|
+
export function UserProfileContainer() {
|
|
63
|
+
const userStore = useIOC(UserStore);
|
|
64
|
+
const user = useStore(userStore, userStore.selector.user);
|
|
65
|
+
|
|
66
|
+
return <UserProfile user={user} />;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 3. 业务组件
|
|
70
|
+
export function LoginForm({ tt }: { tt: LoginI18nInterface }) {
|
|
71
|
+
const userService = useIOC(I.UserServiceInterface);
|
|
72
|
+
|
|
73
|
+
const handleLogin = async (values: LoginFormData) => {
|
|
74
|
+
await userService.login(values);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<Form onFinish={handleLogin}>
|
|
79
|
+
{/* 表单内容 */}
|
|
80
|
+
</Form>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 2. 组件提供者
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// 组合多个提供者
|
|
89
|
+
export function ComboProvider({
|
|
90
|
+
themeConfig,
|
|
91
|
+
children
|
|
92
|
+
}: Props) {
|
|
93
|
+
const mounted = useMountedClient();
|
|
94
|
+
const IOC = clientIOC.create();
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<AntdThemeProvider theme={themeConfig.antdTheme}>
|
|
98
|
+
<ThemeProvider
|
|
99
|
+
themes={themeConfig.supportedThemes}
|
|
100
|
+
defaultTheme={themeConfig.defaultTheme}
|
|
101
|
+
>
|
|
102
|
+
<BootstrapsProvider>
|
|
103
|
+
<AntdRegistry>
|
|
104
|
+
{mounted ? children : null}
|
|
105
|
+
</AntdRegistry>
|
|
106
|
+
</BootstrapsProvider>
|
|
107
|
+
</ThemeProvider>
|
|
108
|
+
</AntdThemeProvider>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 3. 组件接口设计
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
// 1. 组件接口定义
|
|
117
|
+
interface ChatComponentInterface {
|
|
118
|
+
// 属性定义
|
|
119
|
+
messages: MessageInterface[];
|
|
120
|
+
loading?: boolean;
|
|
121
|
+
|
|
122
|
+
// 事件处理
|
|
123
|
+
onSend: (message: string) => void;
|
|
124
|
+
onClear: () => void;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// 2. 组件实现
|
|
128
|
+
@injectable()
|
|
129
|
+
export class ChatComponent implements ChatComponentInterface {
|
|
130
|
+
constructor(
|
|
131
|
+
@inject(ChatStore) private store: ChatStoreInterface,
|
|
132
|
+
@inject(I.Logger) private logger: LoggerInterface
|
|
133
|
+
) {}
|
|
134
|
+
|
|
135
|
+
// 实现接口方法
|
|
136
|
+
async onSend(message: string) {
|
|
137
|
+
try {
|
|
138
|
+
await this.store.sendMessage(message);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
this.logger.error('Failed to send message:', error);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## 状态管理系统
|
|
147
|
+
|
|
148
|
+
### 1. 状态接口
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// 1. 状态接口定义
|
|
152
|
+
export interface StoreStateInterface {
|
|
153
|
+
readonly loading?: boolean;
|
|
154
|
+
readonly error?: Error | null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// 2. 异步状态接口
|
|
158
|
+
export interface AsyncStateInterface<T> {
|
|
159
|
+
loading: boolean;
|
|
160
|
+
result: T | null;
|
|
161
|
+
error: unknown | null;
|
|
162
|
+
startTime: number;
|
|
163
|
+
endTime: number;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// 3. 请求状态实现
|
|
167
|
+
export class RequestState<T = unknown> implements AsyncStateInterface<T> {
|
|
168
|
+
startTime: number;
|
|
169
|
+
endTime: number;
|
|
170
|
+
|
|
171
|
+
constructor(
|
|
172
|
+
public loading: boolean = false,
|
|
173
|
+
public result: T | null = null,
|
|
174
|
+
public error: unknown | null = null
|
|
175
|
+
) {
|
|
176
|
+
this.startTime = Date.now();
|
|
177
|
+
this.endTime = 0;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
end(): this {
|
|
181
|
+
this.endTime = Date.now();
|
|
182
|
+
return this;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### 2. Store 实现
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
// 1. Store 基类
|
|
191
|
+
export abstract class StoreInterface<State extends StoreStateInterface> {
|
|
192
|
+
protected state: State;
|
|
193
|
+
protected subscribers: Set<(state: State) => void>;
|
|
194
|
+
|
|
195
|
+
constructor(initialState: () => State) {
|
|
196
|
+
this.state = initialState();
|
|
197
|
+
this.subscribers = new Set();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// 状态更新
|
|
201
|
+
protected emit(newState: State): void {
|
|
202
|
+
this.state = newState;
|
|
203
|
+
this.subscribers.forEach((subscriber) => subscriber(this.state));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// 选择器
|
|
207
|
+
selector = {
|
|
208
|
+
loading: (state: State) => state.loading,
|
|
209
|
+
error: (state: State) => state.error
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// 2. 具体 Store 实现
|
|
214
|
+
@injectable()
|
|
215
|
+
export class UserStore extends StoreInterface<UserState> {
|
|
216
|
+
constructor(@inject(UserService) private userService: UserServiceInterface) {
|
|
217
|
+
super(() => ({
|
|
218
|
+
user: null,
|
|
219
|
+
loading: false,
|
|
220
|
+
error: null
|
|
221
|
+
}));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async fetchUser(id: string) {
|
|
225
|
+
this.emit({ ...this.state, loading: true });
|
|
226
|
+
try {
|
|
227
|
+
const user = await this.userService.getUser(id);
|
|
228
|
+
this.emit({ ...this.state, user, loading: false });
|
|
229
|
+
} catch (error) {
|
|
230
|
+
this.emit({ ...this.state, error, loading: false });
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### 3. 状态使用
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
// 1. 在组件中使用 Store
|
|
240
|
+
export function UserProfile() {
|
|
241
|
+
const userStore = useIOC(UserStore);
|
|
242
|
+
const user = useStore(userStore, userStore.selector.user);
|
|
243
|
+
const loading = useStore(userStore, userStore.selector.loading);
|
|
244
|
+
|
|
245
|
+
useEffect(() => {
|
|
246
|
+
userStore.fetchUser(userId);
|
|
247
|
+
}, [userStore, userId]);
|
|
248
|
+
|
|
249
|
+
if (loading) return <Loading />;
|
|
250
|
+
if (!user) return <NotFound />;
|
|
251
|
+
|
|
252
|
+
return <UserInfo user={user} />;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// 2. 组合多个 Store
|
|
256
|
+
export function Dashboard() {
|
|
257
|
+
const userStore = useIOC(UserStore);
|
|
258
|
+
const statsStore = useIOC(StatsStore);
|
|
259
|
+
|
|
260
|
+
const user = useStore(userStore, userStore.selector.user);
|
|
261
|
+
const stats = useStore(statsStore, statsStore.selector.stats);
|
|
262
|
+
|
|
263
|
+
return (
|
|
264
|
+
<div>
|
|
265
|
+
<UserWidget user={user} />
|
|
266
|
+
<StatsWidget stats={stats} />
|
|
267
|
+
</div>
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## 组件通信和事件处理
|
|
273
|
+
|
|
274
|
+
### 1. 事件处理
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
// 1. 定义事件接口
|
|
278
|
+
interface ChatEvents {
|
|
279
|
+
onSend: (message: string) => void;
|
|
280
|
+
onClear: () => void;
|
|
281
|
+
onError: (error: Error) => void;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// 2. 实现事件处理
|
|
285
|
+
export function ChatComponent({ onSend, onClear, onError }: ChatEvents) {
|
|
286
|
+
const handleSend = useCallback(async (message: string) => {
|
|
287
|
+
try {
|
|
288
|
+
await onSend(message);
|
|
289
|
+
} catch (error) {
|
|
290
|
+
onError(error as Error);
|
|
291
|
+
}
|
|
292
|
+
}, [onSend, onError]);
|
|
293
|
+
|
|
294
|
+
return (
|
|
295
|
+
<div>
|
|
296
|
+
<ChatInput onSend={handleSend} />
|
|
297
|
+
<ClearButton onClick={onClear} />
|
|
298
|
+
</div>
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### 2. 组件通信
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
// 1. 通过属性传递
|
|
307
|
+
export function ParentComponent() {
|
|
308
|
+
const [data, setData] = useState<Data>();
|
|
309
|
+
|
|
310
|
+
return (
|
|
311
|
+
<ChildComponent
|
|
312
|
+
data={data}
|
|
313
|
+
onUpdate={setData}
|
|
314
|
+
/>
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// 2. 通过 Context 共享
|
|
319
|
+
const ThemeContext = createContext<Theme>(defaultTheme);
|
|
320
|
+
|
|
321
|
+
export function ThemeProvider({ children }: PropsWithChildren) {
|
|
322
|
+
const [theme, setTheme] = useState(defaultTheme);
|
|
323
|
+
|
|
324
|
+
return (
|
|
325
|
+
<ThemeContext.Provider value={{ theme, setTheme }}>
|
|
326
|
+
{children}
|
|
327
|
+
</ThemeContext.Provider>
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## 组件测试和性能优化
|
|
333
|
+
|
|
334
|
+
### 1. 组件测试
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
// 1. 单元测试
|
|
338
|
+
describe('UserProfile', () => {
|
|
339
|
+
it('should render user info', () => {
|
|
340
|
+
const user = { id: '1', name: 'Test' };
|
|
341
|
+
render(<UserProfile user={user} />);
|
|
342
|
+
|
|
343
|
+
expect(screen.getByText(user.name)).toBeInTheDocument();
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it('should handle loading state', () => {
|
|
347
|
+
render(<UserProfile loading />);
|
|
348
|
+
expect(screen.getByTestId('loading')).toBeInTheDocument();
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
// 2. 集成测试
|
|
353
|
+
describe('LoginForm', () => {
|
|
354
|
+
it('should handle login flow', async () => {
|
|
355
|
+
const mockLogin = jest.fn();
|
|
356
|
+
const { getByLabelText, getByRole } = render(
|
|
357
|
+
<LoginForm onLogin={mockLogin} />
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
await userEvent.type(getByLabelText('Email'), 'test@example.com');
|
|
361
|
+
await userEvent.type(getByLabelText('Password'), 'password');
|
|
362
|
+
await userEvent.click(getByRole('button', { name: 'Login' }));
|
|
363
|
+
|
|
364
|
+
expect(mockLogin).toHaveBeenCalledWith({
|
|
365
|
+
email: 'test@example.com',
|
|
366
|
+
password: 'password'
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### 2. 性能优化
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
// 1. 使用 memo 优化渲染
|
|
376
|
+
const UserCard = memo(function UserCard({ user }: UserCardProps) {
|
|
377
|
+
return (
|
|
378
|
+
<div>
|
|
379
|
+
<h3>{user.name}</h3>
|
|
380
|
+
<p>{user.email}</p>
|
|
381
|
+
</div>
|
|
382
|
+
);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// 2. 使用 useMemo 和 useCallback
|
|
386
|
+
function UserList({ users }: UserListProps) {
|
|
387
|
+
const sortedUsers = useMemo(() => {
|
|
388
|
+
return [...users].sort((a, b) => a.name.localeCompare(b.name));
|
|
389
|
+
}, [users]);
|
|
390
|
+
|
|
391
|
+
const handleUserClick = useCallback((userId: string) => {
|
|
392
|
+
// 处理用户点击
|
|
393
|
+
}, []);
|
|
394
|
+
|
|
395
|
+
return (
|
|
396
|
+
<div>
|
|
397
|
+
{sortedUsers.map(user => (
|
|
398
|
+
<UserCard
|
|
399
|
+
key={user.id}
|
|
400
|
+
user={user}
|
|
401
|
+
onClick={handleUserClick}
|
|
402
|
+
/>
|
|
403
|
+
))}
|
|
404
|
+
</div>
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
## 最佳实践和示例
|
|
410
|
+
|
|
411
|
+
### 1. 组件设计原则
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
// 1. 单一职责原则
|
|
415
|
+
// ❌ 错误:组件职责过多
|
|
416
|
+
function UserCard({ user, onEdit, onDelete, onShare }) {
|
|
417
|
+
return (
|
|
418
|
+
<div>
|
|
419
|
+
<UserInfo user={user} />
|
|
420
|
+
<UserActions user={user} />
|
|
421
|
+
<SocialSharing user={user} />
|
|
422
|
+
</div>
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// ✅ 正确:拆分为多个专注的组件
|
|
427
|
+
function UserCard({ user }) {
|
|
428
|
+
return <UserInfo user={user} />;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function UserActions({ user }) {
|
|
432
|
+
return (
|
|
433
|
+
<div>
|
|
434
|
+
<EditButton user={user} />
|
|
435
|
+
<DeleteButton user={user} />
|
|
436
|
+
</div>
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// 2. 组合优于继承
|
|
441
|
+
// ❌ 错误:使用继承
|
|
442
|
+
class SpecialButton extends Button {
|
|
443
|
+
render() {
|
|
444
|
+
return <button className="special">{this.props.children}</button>;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// ✅ 正确:使用组合
|
|
449
|
+
function Button({ variant, children, ...props }) {
|
|
450
|
+
return (
|
|
451
|
+
<button className={`btn-${variant}`} {...props}>
|
|
452
|
+
{children}
|
|
453
|
+
</button>
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### 2. 状态管理最佳实践
|
|
459
|
+
|
|
460
|
+
```typescript
|
|
461
|
+
// 1. 状态隔离
|
|
462
|
+
@injectable()
|
|
463
|
+
export class UserStore extends StoreInterface<UserState> {
|
|
464
|
+
// 将状态逻辑封装在 Store 中
|
|
465
|
+
private async validateUser(user: User): Promise<boolean> {
|
|
466
|
+
return this.validator.validate(user);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
async updateUser(user: User) {
|
|
470
|
+
if (await this.validateUser(user)) {
|
|
471
|
+
this.emit({ ...this.state, user });
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// 2. 选择器模式
|
|
477
|
+
@injectable()
|
|
478
|
+
export class DashboardStore extends StoreInterface<DashboardState> {
|
|
479
|
+
selector = {
|
|
480
|
+
...super.selector,
|
|
481
|
+
activeUsers: (state: DashboardState) =>
|
|
482
|
+
state.users.filter((u) => u.isActive),
|
|
483
|
+
totalRevenue: (state: DashboardState) =>
|
|
484
|
+
state.transactions.reduce((sum, t) => sum + t.amount, 0)
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### 3. 性能优化示例
|
|
490
|
+
|
|
491
|
+
```typescript
|
|
492
|
+
// 1. 虚拟列表
|
|
493
|
+
function VirtualizedList({ items }: Props) {
|
|
494
|
+
return (
|
|
495
|
+
<VirtualScroller
|
|
496
|
+
itemCount={items.length}
|
|
497
|
+
itemSize={50}
|
|
498
|
+
height={400}
|
|
499
|
+
width="100%"
|
|
500
|
+
>
|
|
501
|
+
{({ index, style }) => (
|
|
502
|
+
<div style={style}>
|
|
503
|
+
<ListItem item={items[index]} />
|
|
504
|
+
</div>
|
|
505
|
+
)}
|
|
506
|
+
</VirtualScroller>
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// 2. 懒加载组件
|
|
511
|
+
const LazyUserProfile = lazy(() => import('./UserProfile'));
|
|
512
|
+
|
|
513
|
+
function App() {
|
|
514
|
+
return (
|
|
515
|
+
<Suspense fallback={<Loading />}>
|
|
516
|
+
<LazyUserProfile />
|
|
517
|
+
</Suspense>
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
## 总结
|
|
523
|
+
|
|
524
|
+
项目的组件和状态管理系统遵循以下原则:
|
|
525
|
+
|
|
526
|
+
1. **组件设计**:
|
|
527
|
+
- 清晰的职责划分
|
|
528
|
+
- 可复用的组件接口
|
|
529
|
+
- 类型安全的属性定义
|
|
530
|
+
|
|
531
|
+
2. **状态管理**:
|
|
532
|
+
- 集中的状态管理
|
|
533
|
+
- 响应式的状态更新
|
|
534
|
+
- 类型安全的状态定义
|
|
535
|
+
|
|
536
|
+
3. **性能优化**:
|
|
537
|
+
- 组件级别的优化
|
|
538
|
+
- 状态更新的优化
|
|
539
|
+
- 资源加载的优化
|
|
540
|
+
|
|
541
|
+
4. **最佳实践**:
|
|
542
|
+
- 单一职责原则
|
|
543
|
+
- 组合优于继承
|
|
544
|
+
- 状态隔离原则
|