safety-eval-service-frontend/.cursor/skills/specs/11-接口与数据层规范/03-命名空间与视图连接/SKILL.md

254 lines
7.3 KiB
Markdown
Raw Permalink Normal View History

2026-08-14 16:54:03 +08:00
---
name: jjb-connect
description: 定义 JJB 命名空间与视图连接规范。在使用 Connect 装饰器、defineNamespace、injectActionProps 或 resetModelState 时使用。
---
# 接口与数据层规范
## 命名空间与视图连接
`Connect``@cqsjjb/jjb-dva-runtime` 提供的高阶组件HOC用于将 Dva 模型Model与 React 组件连接。它支持自动注入模型 state、工具方法以及可选的请求 Action简化了组件与数据层的交互。
## 命名空间定义规范
- **集中定义**:命名空间必须集中在 `src/enumerate/namespace/index.js` 中定义
- **定义方式**:使用 `defineNamespace` 函数定义命名空间常量
- **命名规则**:命名空间常量对应 `src/api/<namespace>/index.js` 文件
```javascript
// src/enumerate/namespace/index.js
import { defineNamespace } from '@cqsjjb/jjb-dva-runtime';
// 这里 user 指的是 src/api/user/index.js
export const NS_USER = defineNamespace('user');
// 这里 order 指的是 src/api/order/index.js
export const NS_ORDER = defineNamespace('order');
// 这里 examList 指的是 src/api/examList/index.js
export const NS_EXAM_LIST = defineNamespace('examList');
```
## 请求声明规范
- **文件位置**:每个命名空间常量对应一个 `src/api/<namespace>/index.js` 文件
- **声明方式**:使用 `declareRequest` 声明接口请求(具体用法参考 [`02-接口定义规范`](../02-接口定义规范/SKILL.md)
```javascript
// src/api/user/index.js
import { declareRequest } from '@cqsjjb/jjb-dva-runtime';
// 获取用户信息
export const fetchUserAction = declareRequest(
'loading',
'Get > /api/user/{id}',
'userInfo: {} | res.data || {}'
);
// 新增用户(使用 @ 标记)
export const addUserAction = declareRequest(
'confirmLoading',
'Post > @/api/user'
);
```
## Connect API 说明
- **函数签名**`Connect(models, injectActionProps?)`
- **参数说明**
| 参数 | 类型 | 必填 | 说明 |
| :--- | :--- | :--- | :--- |
| models | Namespace[] | 是 | 命名空间数组,必须从 `src/enumerate/namespace` 引入 |
| injectActionProps | boolean | 否 | 是否自动注入 action 方法,默认为 `false` |
- **injectActionProps 参数说明**
- `false`(默认):只能使用 `dispatchModelAction(namespace, effectAction, payload)` 手动调用
- `true`:所有 `declareRequest` 定义的请求会自动注入到组件 props 中,可直接调用
## 注入的 Props
1. **State 注入**
- `props.user` → 对应 `NS_USER` 模型
- `props.order` → 对应 `NS_ORDER` 模型
- 每个命名空间对应一个 props 属性,包含该模型的所有 state数据字段和 Loading 状态)
2. **工具方法**(始终存在):
- `models`:所有连接的模型
- `getModelState(namespace, name)`:获取模型状态
- `resetModelState(namespace, state)`:重置模型状态
- `dispatchModelAction(namespace, effectAction, payload?)`:手动调用 Action
- `dispatch(type, payload?)`Dva dispatch 方法
3. **Action 方法**(当 `injectActionProps = true` 时才存在):
- 例如 `declareRequest` 定义的 `fetchUserAction` 会在组件 props 中直接可用
- Action 方法返回 Promise使用时需要通过 `success` 字段判断接口是否成功:
```javascript
this.props.xxxAction().then(res => {
// 必须通过 success 判断这个接口是否成功
if (res.success) {
// 接口成功后的处理逻辑
}
});
```
- **注意**如果接口失败了不需要手动抛出接口异常错误http 内部模块已自动拦截并提示错误
## 使用方式
### 方式一默认模式injectActionProps = false - 不推荐
> **说明**:以下示例仅用于对比理解两种方式的区别,实际开发中请使用推荐的方式二。
此时不会自动注入 Action需要手动调用 `dispatchModelAction`
```javascript
import React, { useEffect } from 'react';
import { Connect } from '@cqsjjb/jjb-dva-runtime';
import { NS_USER } from '~/enumerate/namespace';
function UserProfile({ user, dispatchModelAction }) {
useEffect(() => {
dispatchModelAction(NS_USER, 'fetchUserAction', { id: 1 });
}, []);
console.log(user?.loading);
console.log(user?.confirmLoading);
return <div>{user.userInfo?.name}</div>;
}
// 注意这里第二个参数没有传(默认为 false
export default Connect([NS_USER])(UserProfile);
```
### 方式二:自动注入 ActioninjectActionProps = true - 推荐
`injectActionProps = true` 时,模型中通过 `declareRequest` 声明的请求会自动注入到组件 props 中,使用时无需 `dispatchModelAction`,可以直接调用:
```javascript
import React, { useEffect } from 'react';
import { Connect } from '@cqsjjb/jjb-dva-runtime';
import { NS_USER } from '~/enumerate/namespace';
function UserProfile({ user, fetchUserAction }) {
useEffect(() => {
fetchUserAction({ id: 1 });
}, []);
console.log(user?.loading);
console.log(user?.confirmLoading);
return <div>{user.userInfo?.name}</div>;
}
// 第二个参数传 true启用 Action 自动注入到 props
export default Connect([NS_USER], true)(UserProfile);
```
### 类组件装饰器语法
如果要连接 class 类组件,使用装饰器语法:
```javascript
import React, { Component } from 'react';
import { Connect } from '@cqsjjb/jjb-dva-runtime';
import { NS_USER } from '~/enumerate/namespace';
@Connect([NS_USER], true)
class UserProfile extends Component {
componentDidMount() {
this.props.fetchUserAction({ id: 1 });
}
render() {
const { user } = this.props;
return <div>{user.userInfo?.name}</div>;
}
}
export default UserProfile;
```
### 多个模型连接示例
```javascript
import React, { useEffect } from 'react';
import { Connect } from '@cqsjjb/jjb-dva-runtime';
import { NS_USER, NS_ORDER } from '~/enumerate/namespace';
function Dashboard({ user, order, fetchUserAction, fetchOrdersAction }) {
useEffect(() => {
fetchUserAction({ id: 1 });
fetchOrdersAction();
}, []);
return (
<div>
<h3>{user?.userInfo?.name}</h3>
<ul>
{order.list?.map(o => (
<li key={o.id}>{o.title}</li>
))}
</ul>
</div>
);
}
export default Connect([NS_USER, NS_ORDER], true)(Dashboard);
```
### 重置状态示例
```javascript
import { useEffect } from 'react';
import { Connect } from '@cqsjjb/jjb-dva-runtime';
import { NS_USER } from '~/enumerate/namespace';
function UserProfile({ user, fetchUserAction, resetModelState }) {
useEffect(() => {
fetchUserAction({ id: 1 });
return () => {
// 组件销毁时重置状态
resetModelState(NS_USER, {
loading: false,
userInfo: {}
});
};
}, []);
return <div>{user.userInfo?.name}</div>;
}
export default Connect([NS_USER], true)(UserProfile);
```
## 使用规范总结
1. 命名空间必须集中在 `src/enumerate/namespace/index.js` 中定义,使用 `defineNamespace` 函数
2. 请求声明必须放在 `src/api/<namespace>/index.js` 中,使用 `declareRequest`
3. `Connect` 负责把 state、action 方法注入到组件
4. 如果 `injectActionProps = true`state、action 自动注入到组件 props免去手动处理
5. **禁止行为**:禁止在页面内使用 `useState` 管理本应由 `declareRequest` 处理的接口数据或 Loading 状态