资讯专栏INFORMATION COLUMN

(入门)手把手带你更简单的使用dva

makeFoxPlay / 2400人阅读

摘要:介绍首先是一个基于和的数据流方案,然后为了简化开发体验,还额外内置了和,所以也可以理解为一个轻量级的应用框架。

介绍(dva.js)

dva 首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的应用框架。

特性

易学易用,仅有 6 个 api,对 redux 用户尤其友好,配合 umi 使用后更是降低为 0 API

elm 概念,通过 reducers, effects 和 subscriptions 组织 model

插件机制,比如 dva-loading 可以自动处理 loading 状态,不用一遍遍地写 showLoading 和 hideLoading

支持 HMR,基于 babel-plugin-dva-hmr 实现 components、routes 和 models 的 HMR

前言

我的个人理解:dva的核心其实是 saga的封装,将action,reducer等等全部引入到model中。

过多的废话也就不再阐述了,欲知详情,请看官网,本文的目的就是快速开始,让一个拥有react+redux基础的人可以快速使用dva.js

"深入"

配置环境安装依赖之类的就不多说了,请看官方文档

dva new dva-quickstart

我们得到初始项目,目录结构如下:
接下里将会逐个目录解释,请注意看注释

入口文件 index.js
import dva from "dva";   //引入依赖
import "./index.css";

// 1. Initialize
const app = dva();       //初始化 dva应用

// 2. Plugins
// app.use({});          //使用中间件

// 3. Model
// app.model(require("./models/example").default); // 加载model层 (后面详细解释model)

// 4. Router
app.router(require("./router").default);           // 引入router 

// 5. Start
app.start("#root");                                // 挂载dva应用

基本上就是这样,多余的没什么好说的

路由匹配 router.js
import React from "react";
import { Router, Route, Switch } from "dva/router";  // 引入 router,用的就是 react-router
import IndexPage from "./routes/IndexPage";          // 引入路由绑定的高阶组件
 

// 按照从上到下的顺序开始匹配url规则,匹配到了就是展示对应的组件view
function RouterConfig({ history }) {                 
  return (
    
      
        
      
    
  );
}

export default RouterConfig;
路由页面 routes/IndexPage.js

在routes目录下,是路由页面,由多个高阶组件渲染而成,当然,刚初始化的项目自然没有写高阶组件,在后面的实战操作中,我们将以 路由页面 => 高阶组件 => 基础组件 路由绑定model层,高阶组件绑定路由的action事件,基础组件绑定原生事件,在路由中触发action更新数据流 的逻辑 完成一个简单标准的dva过程

import React from "react";
import { connect } from "dva";
import styles from "./IndexPage.css";

// 在这个方法中,我们返回一个dom结构
// 并且 在圆括号中 可以接受一个大对象(包含很多东西),也可以解构 只取其中的state和dispatch,具体可以在后面看
function IndexPage() {
  return (
    

Yay! Welcome to dva!

); } IndexPage.propTypes = { }; // 这里 connect方法就是redux的connect,后面的IndexPage表示绑定的高阶组件 // 在connect的第一个括号中,是可以拿到所有的model对象,这样就可以把对应的model对象绑定到我们的高阶组件上 export default connect()(IndexPage);

看了上面的注释很蒙也没关系,因为纸上谈兵,甚至,我兵都没有出来,你只需要知道,connect的作用及过程就好了

数据模型 model/emample.js
export default {

  namespace: "example",         // 命名空间 作为 connect方法 中获取model对象state的 id

  state: {},                    // 初始化state 

  subscriptions: {              // 订阅
    setup({ dispatch, history }) {  // eslint-disable-line 
    },
  },

  effects: {                     // 异步action的handler
    *fetch({ payload }, { call, put }) {  // eslint-disable-line
      yield put({ type: "save" });
    },
  },

  reducers: {                    //react-redux的reducers 用来接收action并且处理数据更新
    save(state, action) {
      return { ...state, ...action.payload };
    },
  },

};

当我们在高阶组件中通过connect绑定了高阶组件和model,并且在index.js中引入这个model,就可以使用标准流程:
在subscriptions方法中订阅路由变化,当路由与高阶组件相对应,调用effects请求数据,拿到数据reducer更新数据

基础组件 components/Example.js

代码就不贴了,大家应该都知道这里面做什么

公共服务 services/example.js

这里封装了一些公共使用的方法

"浅出"

项目地址:https://github.com/zhaowanhua...

接下来,我们将quick-start项目改造成一个按照dva标准流程的小项目(如上图),帮助大家理解和使用
首先我们把上面那些文件夹下面的文件全部删干净

修改路由模式 index.js
import dva from "dva";
import "./index.css";
import createHistory from "history/createBrowserHistory";

// 这个方法里面 可以配置router的 路由模式,比如hash或者H5 histroy,
// 具体区别可以参考我的文章 vue-router,单页应用原理一致的
const app = dva({
    history: createHistory()
});

// 2. Plugins
// app.use({});

// 3. Model
app.model(require("./models/List").default); // 引入model

// 4. Router
app.router(require("./router").default);

// 5. Start
app.start("#root");
创建基础组件 components/Item.js
import React from "react";

const Item = ({
    num,
    id,
    OnDelete
}) => {
    return (
        
  • OnDelete(id)}> {num}
  • ); }; Item.propTypes = {}; export default Item;
    创建数据模型 models/List.js
    export default {
    
      namespace: "list", // 这个namespace 是model的唯一识别id,在connect中需要使用这个绑定
    
      state: {},
    
      subscriptions: {
        setup({
          dispatch,
          history
        }) { // eslint-disable-line
          return history.listen(({
            pathname
          }) => {
            if (pathname === "/") {
              dispatch({
                type: "fetch",
                payload: {}
              });
            }
          });
        },
      },
    
      effects: {
        * fetch({
          payload
        }, {
          call,
          put
        }) { // eslint-disable-line
          // 这里假装 获取到了服务器的数据
          const fetchData = [0, 1, 2, 3]
          yield put({
            type: "save",
            list: fetchData
          });
        },
      },
    
      reducers: {
        // 保存
        save(state, action) {
          return {...state,
            list: action.list
          };
        },
        // 新增
        add(state, action) {
          const [..._arr] = {...state
          }.list;
          _arr.push(_arr.length)
          return {
            ...state,
            list: _arr
          }
        },
        // 删除
        del(state, action) {
          return {
            ...state,
            list: state.list.filter((item, index) => {
              return index !== action.id
            })
          }
        },
      },
    
    };

    写好model 是要在index.js中引入的,不然没有效果

    创建高阶组件 components/List.js
    import React from "react";
    import Item from "./Item"
    
    // 通过prop 把路由页面的action触发方法绑定过来,传递给子组件(OnDelete),也可以在当前组件触发,如OnAdd
    function List({
      OnAdd,
      OnDelete,
      list
    }) {
      const List = list.map((num, index) => );
    
      return (
        
    {List}
    ); } List.propTypes = {}; export default List;
    创建路由页面 routes/IndexPage.js
    import React from "react";
    import {
      connect
    } from "dva";
    import List from "../components/List"
    
    //我们在路由页面里面渲染高阶组件,写好action,通过prop传递给基础组件
    // 这里引入的list 对应 model中的namespace
    function IndexPage({
      dispatch,
      list
    }) {
      function handleAdd() {
        dispatch({
          type: "list/add"
        });
      }
    
      function handleDelete(id) {
        dispatch({
          type: "list/del",
          id: id,
        });
      }
      return (
        
    ); } IndexPage.propTypes = {}; // 通过connect方法绑定路由页面和model,你可以把connect方法的第一个参数(方法里的) 打印出来看看都有什么东西,不要让解构扰乱了你的眼睛,connect((obj)=>{console.log(obj)})() export default connect(({ list }) => { return list; // 这里是state中的list,通过connect,在每次数据更新的时候,流向我们的view,更新视图,你可以在这里"打桩",看看具体的数据流动 })(IndexPage);
    总结

    以上是我最近学习的想法和思考后得到的内容,希望对大家有所帮助,写的比较随意,在内容中如果有问题或者想法不对,请予指正,也可以提出新的问题,我们共同探究.

    文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

    转载请注明本文地址:https://www.ucloud.cn/yun/96684.html

    相关文章

    • Taro 优秀学习资源汇总

      摘要:多端统一开发框架优秀学习资源汇总官方资源项目仓库官方文档项目仓库官方文档微信小程序官方文档百度智能小程序官方文档支付宝小程序官方文档字节跳动小程序官方文档文章教程不敢阅读包源码带你揭秘背后的哲学从到构建适配不同端微信小程序等的应用小程序最 Awesome Taro 多端统一开发框架 Taro 优秀学习资源汇总 showImg(https://segmentfault.com/img/r...

      toddmark 评论0 收藏0
    • 库,组件,框架 - 收藏集 - 掘金

      摘要:哈哈,我理解,架构就是骨架,如下图所示译年月个有趣的和库前端掘金我们创办的使命是让你及时的了解开发中最新最酷的趋势。 翻译 | 上手 Webpack ? 这篇就够了! - 掘金译者:小 boy (沪江前端开发工程师) 本文原创,转载请注明作者及出处。 原文地址:https://www.smashingmagazine.... JavaSrip... 读 Zepto 源码之代码结构 - ...

      tommego 评论0 收藏0
    • C语言进阶第一问:数据在内存中是如何存储?(把手带你深度剖析数据在内卒中存储,超全解析,码住不

      摘要:在符号位中,表示正,表示负。我们知道对于整型来说,内存中存放的是该数的补码。在计算机系统中,数值一律用补码来表示和存储。表示有效数字,。规定对于位的浮点数,最高的位是 ...

      ghnor 评论0 收藏0
    • 开始学习机器学习之前你必须要了解知识有哪些?机器学习系列入门

      摘要:进入当前程序的学习系统的所有样本称作输入,并组成输入空间。结束语注意这篇文章仅仅是我接下来的机器学习系列的第一篇,后续还会有更多的内容。 往期回顾:统计学习方法第...

      leoperfect 评论0 收藏0

    发表评论

    0条评论

    makeFoxPlay

    |高级讲师

    TA的文章

    阅读更多
    最新活动
    阅读需要支付1元查看
    <