dva 是 怎么在react的基础上搭建的

1,095 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情

 前言

dva 首先是一个基于 redux redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router fetch,所以dva是基于现有应用架构 (redux + react-router + redux-saga 等)的一层轻量封装。

是由阿里架构师 sorrycc 带领 team 完成的一套前端框架。

 需求

快速搭建基于react的项目(PC端,移动端)。

数据流向和最简结构

数据流向

数据的改变发生通常是通过用户交互行为或者浏览器行为(如路由跳转等)触发的,当此类行为会改变数据的时候可以通过 dispatch 发起一个 action,如果是同步行为会直接通过 Reducers 改变 State ,如果是异步行为(副作用)会先触发 Effects 然后流向 Reducers 最终改变 State,所以在 dva 中,数据流向非常清晰简明,并且思路基本跟开源社区保持一致

最简结构

dva = React-Router + Redux + Redux-saga

  • 不带model

import dva from 'dva';

const App = () => <div>Hello dva</div>;


// 创建应用

const app = dva();

// 注册视图

app.router(() => <App />);

// 启动应用

app.start('#root');
  • 带model

// 创建应用

const app = dva();

// 注册 Model

app.model({
  namespace: 'count',

  state: 0,

  reducers: {
    add(state) {
      return state + 1;
    },
  },

  effects: {
    *addAfter1Second(action, { call, put }) {
      yield call(delay, 1000);

      yield put({ type: 'add' });
    },
  },
});

// 注册视图

app.router(() => <ConnectedApp />);

// 启动应用

app.start('#root');

搭建项目

初始化项目

  • 安装node

  • 安装dva-cli

$ npm install dva-cli -g
$ dva -v                           (校验安装的dva版本,我安装的版本是0.10.1)
  • 创建新应用
$dva new dva-quickstart(项目名)
  • 启动项目
cd dva-quickstart(项目名)

npm start

打开如下窗口

  • 安装antd
cnpm install antd babel-plugin-import --save
  • 编辑 .webpackrc,使 babel-plugin-import 插件生效
{

"extraBabelPlugins": [

    ["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }]

]

}

项目架构

|-mock                                   //存放用于 mock 数据的文件 

|-node_modules                     //项目包

|-public                                 //一般用于存放静态文件,打包时会被直接复制到输出目录(./dist)

|-src                                       //项目源代码

  |  |-asserts                           //用于存放静态资源,打包时会经过 webpack 处理

  |  |-caches                            //缓存

  |  |-components                   //组件 存放 React 组件,一般是该项目公用的无状态组件

  |  |-entries                           //入口

  |  |-models                          //数据模型 存放模型文件

  |  |-pages                            //页面视图

  |  |-routes                           //路由 存放需要 connect model 的路由组件

  |  |-services                        //服务 存放服务文件,一般是网络请求等

  |  |-test                              //测试

  |  |-utils                             //辅助工具 工具类库

|-package.json                    //包管理代码

|-webpackrc.js                    //开发配置

|-tsconfig.json                    // ts配置

|-webpack.config.js            //webpack配置 

|-.gitignore                        //Git忽略文件

Dva.js的使用

五个Api(刚配置好时,默认被注释掉,需要取消注释)

import dva from 'dva';

import {message} from 'antd';

import './index.css';

// 1. Initialize 创建 dva 应用实例

const app = dva();

// 2. Plugins 装载插件(可选)

app.use({

 onError: function (error, action) {

   message.error(error.message || '失败', 5);

 }

});

// 3. Model 注册model

app.model(require('../models/example').default);

// 4. Router 配置路由

app.router(require('../routes/router').default);

// 5. Start 启动应用

app.start('#root');

export default app._store; // eslint-disable-line 抛出

app = dva():创建应用,返回 dva 实例。(注:dva 支持多实例)

例如:

 const app = dva({

    history,

    initialState,

    onError,

    onHmr,

});

app.use():配置 hooks 或者注册插件。

app.model():数据逻辑处理,数据流动。

app.router():注册路由表,做路由跳转

app.start([HTMLElement], opts):启动应用

项目编写

  • 定义路由

创建路由,路由可以想象成是组成应用的不同页面

routes文件夹下创建

新建 route component routes/Products.js,内容如下:

import React from 'react';


const Products = (props) => (

<h2>List of Products</h2>

);

export default Products;

添加路由信息到路由表,编辑 router.js :


import Products from './routes/Products';

...

<Route path="/products" exact component={Products} />

然后在浏览器里打开 http://localhost:8000/#/products

  • 编写UI Component

随着应用的发展,会需要在多个页面分享UI元素(或在一个页面使用多次),在dva里可以把这部分抽成component

在components文件下创建

新建 components/ProductList.js 文件:

import React from 'react';

import PropTypes from 'prop-types';

import { Table, Popconfirm, Button } from 'antd';


const ProductList = ({ onDelete, products }) => {

    const columns = [{

        title: 'Name',

        dataIndex: 'name',

    }, {

        title: 'Actions',

        render: (text, record) => {

            return (

                <Popconfirm title="Delete?" onConfirm={() => onDelete(record.id)}>

                <Button>Delete</Button>

                </Popconfirm>

                );

            },
    }];

    return (

        <Table

        dataSource={products}

        columns={columns}

        />

    );

};


ProductList.propTypes = {

    onDelete: PropTypes.func.isRequired,

    products: PropTypes.array.isRequired,

};

export default ProductList;
  • 定义model

完成 UI 后,开始处理数据和逻辑。

dva 通过 model 的概念把一个领域的模型管理起来,包含同步更新 state 的 reducers,处理异步逻辑的 effects,订阅数据源的 subscriptions 。

在models文件夹下创建

新建 model models/products.js

export default {
  namespace: 'products',

  state: [],

  reducers: {
    delete(state, { payload: id }) {
      return state.filter((item) => item.id !== id);
    },
  },
};

这个 model 里:

  • namespace 表示在全局 state 上的 key

  • state 是初始值,在这里是空数组

  • reducers 等同于 redux 里的 reducer,接收 action,同步更新 state

然后别忘记在 index.js 里载入:

app.model(require('./models/products').default);
  • connect起来

dva 提供了 connect 方法。这个 connect 就是 react-redux 的 connect 。

编辑 routes/Products.js 替换为以下内容:

import React from 'react';

import { connect } from 'dva';

import ProductList from '../components/ProductList';

const Products = ({ dispatch, products }) => {
  function handleDelete(id) {
    dispatch({
      type: 'products/delete',

      payload: id,
    });
  }

  return (
    <div>
      <h2>List of Products</h2>

      <ProductList onDelete={handleDelete} products={products} />
    </div>
  );
};

// export default Products;

export default connect(({ products }) => ({
  products,
}))(Products);

最后,需要一些初始数据让这个应用 run 起来。编辑 index.js:

- const app = dva();

const app = dva({
  initialState: {
    products: [
      { name: 'dva', id: 1 },

      { name: 'antd', id: 2 },
    ],
  },
});


  • 构建应用,进行打包
npm run build

  • 运行
npm start