一、环境搭建
-
通过 Umi 集成
- 使用
@umijs/plugin-dva
插件,无需手动安装 Redux 相关依赖。 - Umi 自动扫描
src/models
目录下的模型文件并注册到 Dva 实例 - 配置
.umirc.js
启用插件:
- 使用
src
├── models
│ ├── user.ts # 用户模块模型
│ └── product.ts # 商品模块模型
└── pages
└── index.tsx # 页面组件
/*
在umi项目中符合以下规则的文件会被认为是 model 文件。
src/models 下的文件
src/pages 下,子目录中 models 目录下的文件
src/pages 下,子目录中的 model.js | model.ts
*/
export default {
plugins: [['@umijs/plugin-dva']],
dva: {
immer: true, // 启用 Immer 简化状态更新
hmr: true // 开发环境热更新支持
}
};
immer: true
表示启用 Immer 简化 Reducer 编写
2.手动创建项目
- 安装
dva-cli
脚手架:
npm install dva-cli -g
dva new my-app
支持 TypeScript 和 antd 按需加载
二、Model 定义规范
Dva 的核心是 Model,包含以下 5 个关键属性:
namespace
- 唯一标识符,用于区分不同 Model。
- 示例:
namespace: 'user'
。
state
- 初始状态,必须是纯对象。
- 示例:
state: { list: [] }
。
reducers
- 同步修改
state
,支持普通 Reducer 或 ImmerReducer。 - 唯一可以修改 state 的地方,触发时机是dispatch
- 普通 Reducer(需返回新对象):
reducers: {
updateList(state, { payload }) {
return { ...state, list: payload };
}
}
3 ImmerReducer(直接修改 state
):
reducers: {
updateList: ImmerReducer((state, { payload }) => {
state.list = payload;
})
}
effects
- 处理异步逻辑(如 API 请求),使用
generator
函数。 - *相当于async,yield相当于await
示例:
effects: {
*fetchList({ payload }, { call, put }) {
// select 的参数是一个函数,函数接收的参数是所有model的状态,该条语句的接收值是函数的返回值
const mystate= yield select(store=>store.todo)
// getListReq需要返回promise,options是getListReq的入参
const res= yield call(getListReq,options);
// put和dispatch一样
yield put({ type: 'add', payload: res.list })
}
}
call
执行异步函数,put
触发 Reducer
subscriptions
- 订阅全局事件(如路由变化)。
- 示例:监听路由跳转后加载数据:
subscriptions: {
setup({ dispatch, history }) {
history.listen(({ pathname }) => {
if (pathname === '/user') dispatch({ type: 'fetchList' });
});
}
}
三、数据流管理
组件中触发 Action
- 使用
connect
连接 Model 和组件:
import { connect } from 'dva';
//类组件方案
class UserList extends Component {
componentDidMount() {
this.props.dispatch({ type: 'user/fetchUsers' });
}
render() {
return <div>{this.props.users?.map(user => <Item key={user.id} />)}</div>;
}
}
export default connect(({ user }) => ({ users: user.list }))(UserList);
//函数组件 Hook 方案
import { useSelector, useDispatch } from 'umi';
export default function UserList() {
const users = useSelector(state => state.user.list);
const dispatch = useDispatch();
useEffect(() => {
dispatch({ type: 'user/fetchUsers' });
}, []);
return <div>{users?.map(user => <Item key={user.id} />)}</div>;
}
异步操作顺序控制
- 使用
yield
实现串行请求:
*fetchDetail({ payload }, { call, put }) {
const user = yield call(fetchUser, payload.id);
yield call(fetchProfile, user.token);
}
错误处理
- 局部捕获:使用
try/catch
包裹异步代码。 - 全局捕获:在
src/app.js
中配置onError
:
export const dva = {
config: { onError(e) { console.error(e); } }
};
四、进阶功能
插件机制
dva-loading
:自动管理 loading 状态,无需手动维护。dva-immer
:简化 Reducer 的不可变数据操作45。
动态加载 Model
- 使用
dynamic
实现按需加载 Model:
dynamic(() => import('./models/user'), { loading: LoadingComponent });
五、最佳实践
拆分 Model
- 按业务模块划分 Model(如
user
、order
),避免单一 Model 过大25。
路由与 Model 联动
- 在
subscriptions
中根据路由触发数据加载:
history.listen(() => dispatch({ type: 'init' }));
性能优化
- 使用
useMemo
缓存组件状态,避免重复渲染