React封装框架dvajs(状态管理+异步操作+数据订阅等)

发布于:2025-06-21 ⋅ 阅读:(19) ⋅ 点赞:(0)

目录

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 是一个基于 ReactRedux 的轻量级前端框架,由支付宝团队开发并开源。它整合了 Redux、Redux-Saga、React-Router 等库,并提供了更简洁的开发模式,适用于构建复杂的中后台管理系统。

一、核心特性

1.1 简化 Redux 开发

  • 内置 Redux 状态管理,但通过 model 概念简化了 actionreducereffect(异步逻辑)的定义。

  • 不再需要手动编写 action typesreducerswitch-case

1.2 集成 Redux-Saga

  • 使用 Saga 处理异步逻辑(如 API 请求),支持 async/await 风格。

  • 提供 effects 语法糖,简化 putcalltake 等 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.js

import 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.js

import 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.js

export 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.js

import 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.js

import 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.js

export 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.js

import 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.js

export 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>
  );
}


网站公告

今日签到

点亮在社区的每一天
去签到