Dva.js 上手入门教程(基于 React)
一、Dva.js 简介
Dva.js 是一个基于 React 和 Redux 的轻量级前端框架,封装了 Redux、Redux-saga 和 React-router,简化了状态管理和数据流的处理。它通过约定优于配置的方式,让开发者可以更专注于业务逻辑的实现。
二、安装与初始化项目
1. 安装 Node.js 和 npm
确保你的开发环境已经安装了 Node.js(建议使用 LTS 版本)和 npm(Node.js 包管理器)。
2. 全局安装 Dva-cli
Dva 提供了命令行工具 dva-cli
,用于快速创建和管理 Dva 项目。
npm install -g dva-cli
3. 创建新项目
使用 dva new
命令创建一个新的 Dva 项目。
dva new my-dva-app
cd my-dva-app
4. 启动项目
进入项目目录后,运行以下命令启动开发服务器:
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 # 项目依赖
四、核心概念与代码示例
1. Model(数据模型)
Model 是 Dva 的核心概念,用于管理应用的状态和逻辑。每个 Model 包含以下部分:
namespace
: 命名空间,用于隔离不同 Model 的状态。state
: 初始状态。reducers
: 同步操作,用于更新状态。effects
: 异步操作,通常用于调用 API。subscriptions
: 订阅,用于监听外部事件(如路由变化)。
示例:创建一个计数器 Model
在 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));
}
2. 组件(Component)与连接(Connect)
在 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);
3. 路由配置
在 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;
五、实际业务场景应用
1. 调用 API(异步操作)
在实际项目中,通常需要调用后端 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);
六、总结
- Dva.js 核心概念:
- Model:管理状态和逻辑。
- Reducers:同步操作。
- Effects:异步操作。
- Subscriptions:订阅外部事件。
- 开发流程:
- 定义 Model。
- 编写组件并通过
connect
绑定 Model。 - 配置路由。
- 实际业务:
- 使用
effects
调用 API。 - 使用
reducers
更新状态。 - 使用 Ant Design 等 UI 库快速构建界面。
- 使用
通过以上步骤,你可以快速上手 Dva.js 并开发基于 React 的中大型前端应用。