React封装框架dvajs(状态管理+异步操作+数据订阅等)
目录
DvaJS 简介
一、核心特性
1.1 简化 Redux 开发
1.2 集成 Redux-Saga
1.3 内置 React-Router
1.4 Model 驱动开发
1.5 插件机制
二、基本使用
2.1 安装及创建
2.2 目录结构
2.3 路由使用
2.4 状态管理 models_reducer(同步)(以计数器为例)
2.4.1 创建model
2.4.2 引入model
2.4.3 使用connect进行同步操作
2.5 状态管理 models_effects(异步)
2.5.1 修改model
2.5.2 异步操作
2.6 订阅监听(subscriptions)(以检查登录状态为例)
2.7 模拟数据(mock)
2.7.1 创建mock
2.7.2 注册mock
2.7.3 获取数据
DvaJS 简介
DvaJS 是一个基于 React 和 Redux 的轻量级前端框架,由支付宝团队开发并开源。它整合了 Redux、Redux-Saga、React-Router 等库,并提供了更简洁的开发模式,适用于构建复杂的中后台管理系统。
一、核心特性
1.1 简化 Redux 开发
-
内置 Redux 状态管理,但通过
model
概念简化了action
、reducer
、effect
(异步逻辑)的定义。 -
不再需要手动编写
action types
和reducer
的switch-case
。
1.2 集成 Redux-Saga
-
使用 Saga 处理异步逻辑(如 API 请求),支持
async/await
风格。 -
提供
effects
语法糖,简化put
、call
、take
等 Saga 操作。
1.3 内置 React-Router
-
支持动态路由配置,简化路由管理。
1.4 Model 驱动开发
-
应用状态和逻辑按
model
组织,每个model
包含:-
namespace
(命名空间,类似模块名) -
state
(初始状态) -
reducers
(同步更新状态) -
effects
(异步逻辑) -
subscriptions
(订阅数据,如监听路由变化)
-
1.5 插件机制
-
支持插件扩展(如
dva-loading
自动管理 loading 状态)。
二、基本使用
2.1 安装及创建
通过 npm 安装 dva-cli 并确保版本是 0.9.1 或以上。
// 全局安装
npm install dva-cli -g
// 判断是否安装成功
dva -a
安装完 dva-ci 之后,就可以通过 dva new
创建新应用。
// 创建应用
dva new my-dva
// 进入目录并安装模块
cd my-dva
npm install
运行dva项目
npm start
2.2 目录结构
2.3 路由使用
// src/router.jsimport React from "react";
import { Router, Route, Switch } from "dva/router";
import IndexPage from "./routes/IndexPage";
import UserPage from "./routes/UserPage";function RouterConfig({ history }) {return (<Router history={history}><Switch><Route path="/" exact component={IndexPage} /><Route path="/user" exact component={UserPage} /></Switch></Router>);
}export default RouterConfig;
// src/routes/UserPage.jsimport React from "react";
import { Link } from "dva/router";
export default function UserPage(props) {console.log(props);const handleClick = () => {props.history.push("/"); //函数式组件可以使用useNavigate};return (<div><div>用户页</div><Link to="/">跳转到首页</Link><button onClick={handleClick}>点击跳转到首页</button></div>);
}
2.4 状态管理 models_reducer(同步)(以计数器为例)
2.4.1 创建model
// src/model/count.jsexport default {// 命名空间namespace: "count",// 状态state: {sum: 0,},// 同步操作reducers: {increment(state, action) {// 这里不能直接修改state返回,需要拷贝一个新的let nState = JSON.parse(JSON.stringify(state));nState.sum += action.data.num;return nState;},decrement(state, action) {// 这里不能直接修改state返回,需要拷贝一个新的let nState = JSON.parse(JSON.stringify(state));nState.sum += action.data.num;return nState;},},
};
2.4.2 引入model
// src/index.jsimport dva from "dva";
import "./index.css";// 1. Initialize
const app = dva();// 2. Plugins
// app.use({});// 3. Model
// app.model(require('./models/example').default);
// 引入Model
app.model(require("./models/count").default);// 4. Router
app.router(require("./router").default);// 5. Start
app.start("#root");
2.4.3 使用connect进行同步操作
// src/routes/CountPage.jsimport React from "react";
import { connect } from "dva";
function CountPage(props) {console.log(props);const { sum, dispatch } = props;const inc = (num) => {dispatch({ type: "count/increment", data: { num } });};const dec = (num) => {dispatch({ type: "count/decrement", data: { num } });};return (<div><div>{sum}</div><button onClick={() => inc(1)}>+1</button><button onClick={() => dec(-2)}>-2</button></div>);
}const mapStateToProps = (state) => {return {sum: state.count.sum, // count是命名空间,num是状态};
};export default connect(mapStateToProps)(CountPage);
2.5 状态管理 models_effects(异步)
2.5.1 修改model
// src/model/count.jsexport default {// 命名空间namespace: "count",// 状态state: {sum: 0,},// 同步操作reducers: {increment(state, action) {// 这里不能直接修改state返回,需要拷贝一个新的let nState = JSON.parse(JSON.stringify(state));nState.sum += action.data.num;return nState;},decrement(state, action) {// 这里不能直接修改state返回,需要拷贝一个新的let nState = JSON.parse(JSON.stringify(state));nState.sum += action.data.num;return nState;},},// 异步操作effects: {// 这里第一个结构赋值的data 要与dispatch一致*fetchIncrement({ data }, { put, call }) {// 异步操作,这里假设是1s延迟,会将结果返回给res// call方法异步操作,第1个是函数,第2个是传参const res = yield call((_) => {return new Promise((resolve) => {setTimeout(() => {resolve(_);}, 1000);});}, data);// 获得结果后调用同步方法,修改状态yield put({ type: "increment", data: { num: res.num } });},},
};
2.5.2 异步操作
// src/routes/CountPage.jsimport React from "react";
import { connect } from "dva";
function CountPage(props) {console.log(props);const { sum, dispatch } = props;const inc = (num) => {dispatch({ type: "count/increment", data: { num } });};const dec = (num) => {dispatch({ type: "count/decrement", data: { num } });};const fetchInc = (num) => {dispatch({ type: "count/fetchIncrement", data: { num } });};return (<div><div>{sum}</div><button onClick={() => inc(1)}>+1</button><button onClick={() => dec(-2)}>-2</button><button onClick={() => fetchInc(2)}>异步+2</button></div>);
}const mapStateToProps = (state) => {return {sum: state.count.sum, // count是命名空间,num是状态};
};export default connect(mapStateToProps)(CountPage);
2.6 订阅监听(subscriptions)(以检查登录状态为例)
// src/models/global.jsexport default {// 命名空间namespace: "global",// 状态state: {},subscriptions: {setup({ dispatch, history }) {// 初始化监听const unlisten = history.listen((location) => {console.log("路由变化:", location.pathname);// 示例:检查登录状态const token = localStorage.getItem("token");if (!token && location.pathname !== "/login") {history.push("/login");}});// 返回取消监听函数(可选)return () => unlisten();},},
};
2.7 模拟数据(mock)
2.7.1 创建mock
my-dva/mock.js
注意:不在src下,是在项目根目录下
module.exports = {"GET /api/mockData": (req, res) => {console.log(req);res.send({name: "喜羊羊",});},
};
2.7.2 注册mock
./.roadhogrc.mock.js
注意:不在src下,是在项目根目录下
export default {...require("./mock/testMock"),
};
src/serivces/example.js
import request from "../utils/request";// 注册mock接口
export function testMock() {return request("/api/mockData");
}
2.7.3 获取数据
import React from "react";
import { Link } from "dva/router";
import { testMock } from "../services/example";
export default function UserPage(props) {testMock().then((res) => {console.log(res);});const handleClick = () => {props.history.push("/"); //函数式组件可以使用useNavigate};return (<div><div>用户页</div><Link to="/">跳转到首页</Link><button onClick={handleClick}>点击跳转到首页</button></div>);
}