Dva.js 是一个基于 React 和 Redux 的轻量级前端框架,封装了 Redux、Redux-saga 和 React-router,简化了状态管理和数据流的处理。它通过约定优于配置的方式,让开发者可以更专注于业务逻辑的实现。
确保你的开发环境已经安装了 Node.js(建议使用 LTS 版本)和 npm(Node.js 包管理器)。
Dva 提供了命令行工具 dva-cli
,用于快速创建和管理 Dva 项目。
npm install -g dva-cli
使用 dva new
命令创建一个新的 Dva 项目。
dva new my-dva-app
cd my-dva-app
进入项目目录后,运行以下命令启动开发服务器:
npm start
项目启动后,默认会在浏览器中打开 http://localhost:8000
,显示 Dva 的默认页面。
Dva 项目的默认目录结构如下:
my-dva-app/
├── src/
│ ├── components/ # 公共组件
│ ├── models/ # 数据模型(核心)
│ ├── routes/ # 页面组件
│ ├── services/ # API 服务
│ ├── utils/ # 工具函数
│ ├── index.js # 项目入口文件
│ └── router.js # 路由配置
├── .editorconfig # 编辑器配置
├── .eslintrc # ESLint 配置
├── .roadhogrc # 打包配置
└── package.json # 项目依赖
Model 是 Dva 的核心概念,用于管理应用的状态和逻辑。每个 Model 包含以下部分:
namespace
: 命名空间,用于隔离不同 Model 的状态。state
: 初始状态。reducers
: 同步操作,用于更新状态。effects
: 异步操作,通常用于调用 API。subscriptions
: 订阅,用于监听外部事件(如路由变化)。在 src/models/counter.js
中定义:
export default {
namespace: 'counter', // 命名空间
state: { // 初始状态
count: 0,
},
reducers: { // 同步操作
add(state) { // 更新状态,必须返回新对象
return { ...state, count: state.count + 1 };
},
minus(state) { // 减少计数
return { ...state, count: state.count - 1 };
},
},
effects: { // 异步操作
*addAsync({ payload }, { call, put }) { // Generator 函数
yield call(delay, 1000); // 模拟异步操作(延迟 1 秒)
yield put({ type: 'add' }); // 触发同步操作
},
},
subscriptions: { // 订阅
setup({ dispatch, history }) {
return history.listen(({ pathname }) => {
if (pathname === '/') {
dispatch({ type: 'init' }); // 初始化操作
}
});
},
},
};
// 辅助函数:延迟
function delay(timeout) {
return new Promise(resolve => setTimeout(resolve, timeout));
}
在 React 组件中,通过 dva
的 connect
方法将 Model 的状态和操作绑定到组件的 props
上。
在 src/routes/CounterPage.js
中定义:
import React from 'react';
import { connect } from 'dva';
import { Button } from 'antd'; // 使用 Ant Design 组件
function CounterPage({ count, dispatch }) {
return (
<div style={{ padding: '20px' }}>
<h2>当前计数: {count}</h2>
<Button
type="primary"
onClick={() => dispatch({ type: 'counter/add' })}
style={{ marginRight: '10px' }}
>
增加
</Button>
<Button
onClick={() => dispatch({ type: 'counter/minus' })}
style={{ marginRight: '10px' }}
>
减少
</Button>
<Button
type="dashed"
onClick={() => dispatch({ type: 'counter/addAsync' })}
>
异步增加(1秒后)
</Button>
</div>
);
}
// 将 Model 的状态和操作绑定到组件的 props
export default connect(({ counter }) => ({
count: counter.count, // 从 counter Model 的 state 中获取 count
}))(CounterPage);
在 src/router.js
中配置路由:
import React from 'react';
import { Router, Route, Switch } from 'dva/router';
import CounterPage from './routes/CounterPage';
function RouterConfig({ history }) {
return (
<Router history={history}>
<Switch>
<Route path="/" exact component={CounterPage} />
</Switch>
</Router>
);
}
export default RouterConfig;
在实际项目中,通常需要调用后端 API 获取数据。Dva 的 effects
用于处理异步操作。
定义 Service
在 src/services/user.js
中定义 API 调用:
import request from '../utils/request'; // 封装好的请求工具
export async function fetchUsers() {
return request('/api/users'); // 返回 Promise
}
定义 Model
在 src/models/user.js
中定义:
import { fetchUsers } from '../services/user';
export default {
namespace: 'user',
state: {
list: [],
loading: false,
},
effects: {
*fetch({ payload }, { call, put }) {
yield put({ type: 'showLoading' }); // 显示加载状态
try {
const response = yield call(fetchUsers, payload);
yield put({ type: 'save', payload: response.data }); // 保存数据
} catch (error) {
console.error('获取用户列表失败:', error);
} finally {
yield put({ type: 'hideLoading' }); // 隐藏加载状态
}
},
},
reducers: {
save(state, { payload }) {
return { ...state, list: payload };
},
showLoading(state) {
return { ...state, loading: true };
},
hideLoading(state) {
return { ...state, loading: false };
},
},
};
定义组件
在 src/routes/UserPage.js
中定义:
import React from 'react';
import { connect } from 'dva';
import { Table, Spin } from 'antd';
function UserPage({ list, loading, dispatch }) {
React.useEffect(() => {
dispatch({ type: 'user/fetch' }); // 组件挂载时获取数据
}, []);
const columns = [
{ title: 'ID', dataIndex: 'id', key: 'id' },
{ title: '姓名', dataIndex: 'name', key: 'name' },
{ title: '邮箱', dataIndex: 'email', key: 'email' },
];
return (
<div style={{ padding: '20px' }}>
<h2>用户列表</h2>
<Spin spinning={loading}>
<Table
dataSource={list}
columns={columns}
rowKey="id"
/>
</Spin>
</div>
);
}
export default connect(({ user }) => ({
list: user.list,
loading: user.loading,
}))(UserPage);
connect
绑定 Model。effects
调用 API。reducers
更新状态。通过以上步骤,你可以快速上手 Dva.js 并开发基于 React 的中大型前端应用。