Skip to content

Commit 8df6622

Browse files
committed
feat: 添加前端技术栈规范文档,包含React、Apollo、Chakra UI和MobX相关指南
1 parent 8ee5203 commit 8df6622

36 files changed

+985
-117
lines changed
Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
---
2-
description: 利用 Apollo Client 的缓存功能来提高性能
2+
description: "利用 Apollo Client 的缓存功能来提高性能"
33
globs: src/**/*.js
44
---
5-
- 利用 Apollo Client 的缓存功能
5+
- **利用 Apollo Client 的缓存功能**:
6+
- 优先使用 Apollo Client 的规范化缓存(Normalized Cache)来存储和管理数据,减少不必要的网络请求。
7+
- 理解并配置 `fetchPolicy`,以优化数据获取策略,例如 `cache-first`、`network-only`、`cache-and-network` 等。
8+
- 针对频繁变动的数据,考虑使用 `no-cache` 或 `cache-and-network` 策略,并结合订阅(Subscriptions)或轮询(Polling)实现实时更新。
9+
- 对于列表数据,合理使用 `offsetLimitPagination` 或 `cursorPagination` 等策略来处理分页和无限滚动,确保缓存的有效性。
10+
- 在进行数据修改(如 Mutations)后,通过 `update` 函数或 `refetchQueries` 来及时更新缓存,保持UI与后端数据的一致性。
Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,32 @@
11
---
2-
description: 指导为 Apollo 操作实现自定义 hooks
2+
description: "指导为 Apollo 操作实现自定义 hooks"
33
globs: src/hooks/**/*.js
44
---
5-
- 为 Apollo 操作实现自定义 hooks
5+
- **为 Apollo 操作实现自定义 hooks**:
6+
- **封装复杂逻辑**:将重复的 Apollo Client 操作(如查询、变更、订阅)和相关逻辑封装到自定义 hooks 中,提高代码复用性。
7+
- **统一错误处理和加载状态**:在自定义 hooks 中统一处理加载(`loading`)、错误(`error`)状态和数据(`data`),简化组件层的逻辑。
8+
- **类型安全**:结合 TypeScript 为自定义 hooks 定义清晰的输入(变量)和输出(数据、状态)类型,确保类型安全。
9+
- **可测试性**:设计易于测试的自定义 hooks,方便进行单元测试和集成测试。
10+
- **示例**:
11+
```typescript
12+
// hooks/useUser.ts
13+
import { useQuery, gql } from '@apollo/client';
14+
15+
const GET_USER_QUERY = gql`
16+
query GetUser($id: ID!) {
17+
user(id: $id) {
18+
id
19+
name
20+
email
21+
}
22+
}
23+
`;
24+
25+
export const useUser = (id: string) => {
26+
const { data, loading, error } = useQuery(GET_USER_QUERY, {
27+
variables: { id },
28+
});
29+
30+
return { user: data?.user, loading, error };
31+
};
32+
```
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
---
2-
description: 建议使用 Apollo Client DevTools 进行调试
2+
description: "建议使用 Apollo Client DevTools 进行调试"
33
globs: src/**/*.js
44
---
5-
- 使用 Apollo Client DevTools 进行调试
5+
- **使用 Apollo Client DevTools 进行调试**:
6+
- **安装与集成**:在开发环境中安装并集成 Apollo Client DevTools 浏览器扩展,以便于调试 Apollo Client 的操作和缓存状态。
7+
- **查询与变更监控**:利用 DevTools 监控所有 GraphQL 查询(Queries)、变更(Mutations)和订阅(Subscriptions)的请求与响应,包括变量、操作名称和返回数据。
8+
- **缓存检查**:检查 Apollo Client 的规范化缓存(Normalized Cache)内容,理解数据如何存储、更新和失效,有助于排查数据不一致问题。
9+
- **性能分析**:分析 GraphQL 请求的性能,识别潜在的瓶颈,例如慢查询或过多的网络请求。
10+
- **错误诊断**:查看 GraphQL 错误信息,快速定位后端或前端数据处理中的问题。
11+
- **状态管理**:观察组件如何与 Apollo Client 交互,以及数据流如何在应用中传递。
Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,29 @@
11
---
2-
description: 要求在应用程序根部使用 Apollo Provider
2+
description: "要求在应用程序根部使用 Apollo Provider"
33
globs: src/App.jsx
44
---
5-
- 在应用程序根部使用 Apollo Provider
5+
- **在应用程序根部使用 Apollo Provider**:
6+
- **全局可用性**:确保在 React 应用程序的根组件(例如 `App.js` 或 `index.js`)中使用 `ApolloProvider` 包裹整个应用,以便所有子组件都能访问 Apollo Client 实例。
7+
- **Client 实例配置**:在 `ApolloProvider` 中传入已配置好的 `ApolloClient` 实例,该实例应包含 `uri`(GraphQL 服务器地址)和 `cache`(缓存策略,通常是 `InMemoryCache`)等必要配置。
8+
- **示例**:
9+
```javascript
10+
// index.js 或 App.js
11+
import React from 'react';
12+
import ReactDOM from 'react-dom/client';
13+
import { ApolloClient, InMemoryCache, ApolloProvider, gql } from '@apollo/client';
14+
import App from './App';
15+
16+
const client = new ApolloClient({
17+
uri: 'YOUR_GRAPHQL_ENDPOINT',
18+
cache: new InMemoryCache(),
19+
});
20+
21+
ReactDOM.createRoot(document.getElementById('root')).render(
22+
<React.StrictMode>
23+
<ApolloProvider client={client}>
24+
<App />
25+
</ApolloProvider>
26+
</React.StrictMode>,
27+
);
28+
```
29+
- **中间件与错误处理**:在 `ApolloClient` 配置中,可以添加 `link` 来处理认证、错误重试、文件上传等高级功能,例如使用 `HttpLink`、`AuthLink`、`ErrorLink` 等。
Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,62 @@
11
---
2-
description: 应用 GraphQL 和 Apollo Client 使用的最佳实践,包括状态管理、数据获取和错误处理
2+
description: "应用 GraphQL 和 Apollo Client 使用的最佳实践,包括状态管理、数据获取和错误处理"
33
globs: src/graphql/**/*.js
44
---
5-
- 使用 Apollo Client 进行状态管理和数据获取
6-
- 实现查询组件进行数据获取
7-
- 利用变更操作进行数据修改
8-
- 使用片段创建可重用的查询部分
9-
- 实现适当的错误处理和加载状态
5+
- **使用 Apollo Client 进行状态管理和数据获取**:
6+
- 利用 `useQuery` Hook 进行数据查询,处理加载状态、错误和数据。
7+
- 使用 `useMutation` Hook 执行数据修改操作,并在成功后更新缓存或重新获取相关数据。
8+
- 结合 `useSubscription` Hook 实现实时数据更新。
9+
- **实现查询组件进行数据获取**:
10+
- 推荐使用基于 Hook 的方式(如 `useQuery`)而非旧版 `Query` 组件,以更好地利用 React Hooks 的特性。
11+
- 示例:
12+
```typescript
13+
import { useQuery, gql } from '@apollo/client';
14+
15+
const GET_TODOS = gql`
16+
query GetTodos {
17+
todos {
18+
id
19+
text
20+
completed
21+
}
22+
}
23+
`;
24+
25+
function TodosList() {
26+
const { loading, error, data } = useQuery(GET_TODOS);
27+
28+
if (loading) return <p>Loading...</p>;
29+
if (error) return <p>Error :({error.message}</p>;
30+
31+
return (
32+
<ul>
33+
{data.todos.map(({ id, text, completed }) => (
34+
<li key={id} style={{ textDecoration: completed ? 'line-through' : 'none' }}>
35+
{text}
36+
</li>
37+
))}
38+
</ul>
39+
);
40+
}
41+
```
42+
- **利用变更操作进行数据修改**:
43+
- 使用 `useMutation` Hook 发送变更请求,并利用 `update` 函数或 `refetchQueries` 选项在变更成功后更新 Apollo 缓存,确保 UI 状态与后端数据同步。
44+
- **使用片段创建可重用的查询部分**:
45+
- 定义 GraphQL 片段(Fragments)来封装可重用的字段集,提高查询的可维护性和复用性。
46+
- 示例:
47+
```graphql
48+
fragment UserFields on User {
49+
id
50+
name
51+
email
52+
}
53+
54+
query GetUserDetails($id: ID!) {
55+
user(id: $id) {
56+
...UserFields
57+
}
58+
}
59+
```
60+
- **实现适当的错误处理和加载状态**:
61+
- 在组件中处理 `loading` 和 `error` 状态,向用户提供友好的加载指示和错误反馈。
62+
- 可以结合 `ErrorLink` 或自定义错误边界(Error Boundaries)来集中处理 GraphQL 错误。
Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,58 @@
11
---
2-
description: 要求为 GraphQL 错误实现适当的错误边界
2+
description: "要求为 GraphQL 错误实现适当的错误边界"
33
globs: src/**/*.jsx
4-
---
5-
- 为 GraphQL 错误实现适当的错误边界
4+
---
5+
- **为 GraphQL 错误实现适当的错误边界**:
6+
- **使用 React 错误边界**:利用 React 的错误边界(Error Boundaries)来捕获组件树中由 GraphQL 操作(查询、变更、订阅)引起的 JavaScript 错误,防止整个应用崩溃。
7+
- **集中错误处理**:在错误边界组件中集中处理和展示友好的错误信息,而不是让每个组件单独处理错误状态。
8+
- **错误日志记录**:在错误边界中实现错误日志记录机制(例如,发送到 Sentry、Bugsnag 等错误监控服务),以便于跟踪和诊断生产环境中的问题。
9+
- **用户体验**:当发生错误时,向用户显示一个回退 UI,提供重试选项或引导用户进行下一步操作,提升用户体验。
10+
- **示例**:
11+
```javascript
12+
// ErrorBoundary.js
13+
import React from 'react';
14+
15+
class ErrorBoundary extends React.Component {
16+
constructor(props) {
17+
super(props);
18+
this.state = { hasError: false, error: null, errorInfo: null };
19+
}
20+
21+
static getDerivedStateFromError(error) {
22+
// 更新 state 使下一次渲染能够显示降级后的 UI
23+
return { hasError: true };
24+
}
25+
26+
componentDidCatch(error, errorInfo) {
27+
// 你同样可以将错误日志上报给服务器
28+
console.error("Uncaught error:", error, errorInfo);
29+
this.setState({ error, errorInfo });
30+
}
31+
32+
render() {
33+
if (this.state.hasError) {
34+
// 你可以自定义降级后的 UI 并渲染
35+
return (
36+
<div style={{ padding: '20px', border: '1px solid red', color: 'red' }}>
37+
<h2>Something went wrong.</h2>
38+
<details style={{ whiteSpace: 'pre-wrap' }}>
39+
{this.state.error && this.state.error.toString()}
40+
<br />
41+
{this.state.errorInfo.componentStack}
42+
</details>
43+
</div>
44+
);
45+
}
46+
47+
return this.props.children;
48+
}
49+
}
50+
51+
export default ErrorBoundary;
52+
53+
// 在应用中使用
54+
// <ErrorBoundary>
55+
// <MyGraphQLComponent />
56+
// </ErrorBoundary>
57+
```
58+
- **与 Apollo Link 结合**:对于 GraphQL 网络层面的错误(如 HTTP 错误、GraphQL 格式错误),可以结合 Apollo Client 的 `ErrorLink` 进行处理,并在 `ErrorLink` 中抛出错误,使其能被 React 错误边界捕获。
Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,30 @@
11
---
2-
description: 要求遵循查询、变更和片段的命名约定
2+
description: "要求遵循查询、变更和片段的命名约定"
33
globs: src/graphql/**/*.js
44
---
5-
- 遵循查询、变更和片段的命名约定
5+
- **遵循查询、变更和片段的命名约定**:
6+
- **查询(Queries)**:
7+
- 使用 `get` 或 `fetch` 前缀,后跟实体名称和可选的复数形式。
8+
- 示例:`getUsers`、`getUserById`、`fetchProductDetails`。
9+
- **变更(Mutations)**:
10+
- 使用动词前缀表示操作类型,后跟实体名称。
11+
- 示例:`createUser`、`updateProduct`、`deleteComment`。
12+
- **订阅(Subscriptions)**:
13+
- 使用 `on` 前缀,后跟事件名称。
14+
- 示例:`onNewMessage`、`onUserUpdated`。
15+
- **片段(Fragments)**:
16+
- 使用实体名称,后跟 `Fields` 或 `Details` 后缀。
17+
- 示例:`UserFields`、`ProductDetails`。
18+
- **类型(Types)和字段(Fields)**:
19+
- 类型名称使用 PascalCase(大驼峰命名法)。
20+
- 字段名称使用 camelCase(小驼峰命名法)。
21+
- 示例:`User` 类型中的 `firstName` 字段。
22+
- **枚举(Enums)**:
23+
- 枚举类型名称使用 PascalCase,枚举值使用 ALL_CAPS(全大写)。
24+
- 示例:`OrderStatus` 枚举中的 `PENDING`、`COMPLETED`。
25+
- **变量(Variables)**:
26+
- 变量名称使用 camelCase。
27+
- 示例:`$userId`、`$input`。
28+
- **通用原则**:
29+
- 保持命名清晰、简洁、一致,能够直观地表达其用途和所操作的数据。
30+
- 避免缩写,除非是广为人知的缩写(如 `ID`)。
Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,79 @@
11
---
2-
description: 强制使用 TypeScript 确保 GraphQL 操作的类型安全
2+
description: "强制使用 TypeScript 确保 GraphQL 操作的类型安全"
33
globs: src/graphql/**/*.ts
44
---
5-
- 对 GraphQL 操作使用 TypeScript 确保类型安全
5+
- **对 GraphQL 操作使用 TypeScript 确保类型安全**:
6+
- **代码生成**:利用 GraphQL Code Generator 或类似工具,根据 GraphQL schema 和操作(Queries, Mutations, Subscriptions)自动生成 TypeScript 类型定义。
7+
- **类型推断**:确保 `useQuery`、`useMutation` 和 `useSubscription` Hooks 的返回值(`data`, `loading`, `error`)和变量(`variables`)都具有正确的类型推断。
8+
- **端到端类型安全**:从 GraphQL API 到前端组件,实现端到端的类型安全,减少运行时错误。
9+
- **示例**:
10+
```typescript
11+
// graphql.schema.json (部分)
12+
{
13+
"data": {
14+
"__schema": {
15+
"types": [
16+
{
17+
"kind": "OBJECT",
18+
"name": "User",
19+
"fields": [
20+
{ "name": "id", "type": { "kind": "NON_NULL", "ofType": { "kind": "SCALAR", "name": "ID" } } },
21+
{ "name": "name", "type": { "kind": "SCALAR", "name": "String" } }
22+
]
23+
}
24+
]
25+
}
26+
}
27+
}
28+
29+
// query.graphql
30+
query GetUser($id: ID!) {
31+
user(id: $id) {
32+
id
33+
name
34+
}
35+
}
36+
37+
// generated.ts (通过工具生成)
38+
export type GetUserQueryVariables = {
39+
id: string;
40+
};
41+
42+
export type GetUserQuery = {
43+
user?: {
44+
__typename?: "User";
45+
id: string;
46+
name?: string | null;
47+
} | null;
48+
};
49+
50+
// component.tsx
51+
import { useQuery } from '@apollo/client';
52+
import { GetUserQuery, GetUserQueryVariables } from './generated'; // 假设类型已生成
53+
54+
const GET_USER_QUERY = gql`
55+
query GetUser($id: ID!) {
56+
user(id: $id) {
57+
id
58+
name
59+
}
60+
}
61+
`;
62+
63+
function UserProfile({ userId }: { userId: string }) {
64+
const { data, loading, error } = useQuery<GetUserQuery, GetUserQueryVariables>(GET_USER_QUERY, {
65+
variables: { id: userId },
66+
});
67+
68+
if (loading) return <p>Loading...</p>;
69+
if (error) return <p>Error: {error.message}</p>;
70+
71+
return (
72+
<div>
73+
<h1>{data?.user?.name}</h1>
74+
<p>ID: {data?.user?.id}</p>
75+
</div>
76+
);
77+
}
78+
```
79+
- **手动类型定义(不推荐)**:在无法使用代码生成工具的情况下,可以手动定义 GraphQL 响应的 TypeScript 接口,但这容易出错且难以维护。
Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,33 @@
11
---
2-
description: 在 React 组件中强制使用带有 hooks 的函数式组件
2+
description: "在 React 组件中强制使用带有 hooks 的函数式组件"
33
globs: src/components/**/*.jsx
44
---
5-
- 始终使用带有 hooks 的函数式组件而不是类组件。
5+
- **始终使用带有 hooks 的函数式组件而不是类组件**:
6+
- **现代化开发**:优先使用 React Hooks(如 `useState`, `useEffect`, `useContext`, `useReducer`, `useCallback`, `useMemo` 等)进行状态管理和副作用处理,以符合 React 官方推荐的现代化开发范式。
7+
- **代码简洁性**:函数式组件通常比类组件更简洁,更易于阅读和理解,尤其是在处理逻辑复用时。
8+
- **逻辑复用**:通过自定义 Hooks,可以方便地在不同组件之间共享状态逻辑,避免了高阶组件(HOCs)和渲染属性(Render Props)带来的嵌套问题。
9+
- **性能优化**:结合 `useCallback` 和 `useMemo` 等 Hooks,可以有效地进行性能优化,避免不必要的渲染。
10+
- **可测试性**:函数式组件和 Hooks 更容易进行单元测试。
11+
- **示例**:
12+
```javascript
13+
// 函数式组件示例
14+
import React, { useState, useEffect } from 'react';
15+
16+
function Counter() {
17+
const [count, setCount] = useState(0);
18+
19+
useEffect(() => {
20+
document.title = `You clicked ${count} times`;
21+
}, [count]);
22+
23+
return (
24+
<div>
25+
<p>You clicked {count} times</p>
26+
<button onClick={() => setCount(count + 1)}>
27+
Click me
28+
</button>
29+
</div>
30+
);
31+
}
32+
```
33+
- **特殊情况**:仅在极少数需要使用 `getSnapshotBeforeUpdate` 或 `componentDidCatch` 等生命周期方法时,才考虑使用类组件,但大多数场景下 Hooks 都能提供替代方案(如错误边界)。

0 commit comments

Comments
 (0)