Dva.js详解

一、环境搭建

  1. 通过 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(如 userorder),避免单一 Model 过大25。

路由与 Model 联动

  • 在 subscriptions 中根据路由触发数据加载:
history.listen(() => dispatch({ type: 'init' }));

 ‌性能优化

  • 使用 useMemo 缓存组件状态,避免重复渲染

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值