dva react 框架_react dva框架 - CSDN
  • 学习react前端框架dva

    2019-03-18 13:18:40
    dva 是由阿里架构师 sorrycc 带领 team 完成的一套前端框架,在作者的 github 里是这么描述它的:“dvareact 和 redux 的最佳实践”。 一.介绍 1.What's dva ? dva 是基于现有应用架构 (redux + react-router...

     dva 是由阿里架构师 sorrycc 带领 team 完成的一套前端框架,在作者的 github 里是这么描述它的:“dva 是 react 和 redux 的最佳实践”。


    一.介绍

    1.What's dva ?

    dva 是基于现有应用架构 (redux + react-router + redux-saga 等)的一层轻量封装。dva 是 react 和 redux 的最佳实践。最核心的是提供了 app.model 方法,用于把 reducer, initialState, action, saga 封装到一起。官网    dva = React-Router + Redux + Redux-saga

    2.安装

    1.安装 dva-cli
    npm install dva-cli -g
    2.扎到安装项目的目录
    cd ylz_project/my_reactdemo
    3.创建项目:Dva-test项目名
    dva new Dva-test
    4.进入项目
    cd Dva-test
    5.启动项目
    npm  start

     成功。

    3.项目结构

    二.概念

    下面都是官网的知识点

    (1).数据流向

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

    (2).Model

    model 是 dva 中最重要的概念,Model 非 MVC 中的 M,而是领域模型,用于把数据相关的逻辑聚合到一起,几乎所有的数据,逻辑都在这边进行处理分发

    1.State

    State 表示 Model 的状态数据,通常表现为一个 javascript 对象(当然它可以是任何值);操作的时候每次都要当作不可变数据(immutable data)来对待,保证每次都是全新对象,没有引用关系,这样才能保证 State 的独立性,便于测试和追踪变化。

    // dva()初始化
    const app = dva({
      initialState: { count: 1 },
    });
    
    // modal()定义事件
    app.model({
      namespace: 'count',
      state: 0,
    });
    
    //初始值,我们在 dva() 初始化的时候和在 modal 里面的 state 对其两处进行定义,其中  modal 中的优先级低于传给  dva() 的  opts.initialState

    2.Action   :type dispatch = (a: Action) => Action

    Action 是一个普通 javascript 对象,它是改变 State 的唯一途径。表示操作事件,可以是同步,也可以是异步
    action 的格式如下,它需要有一个 type ,表示这个 action 要触发什么操作;payload 则表示这个 action 将要传递的数据

    我们通过 dispatch 方法来发送一个 action

    /*Action
    Action 表示操作事件,可以是同步,也可以是异步
     {
      type: String,
      payload: data
    }
    */
    dispatch(Action);
    dispatch({ type: 'todos/add', payload: 'Learn Dva' });
    

    其实我们可以构建一个Action 创建函数,如下 

    const  USER-LIST  = 'USER-LIST'//用户列表
    //action 函数
    function  user(data){
    	return {type:MSG_READ ,payload:data}
    }
    
    dispatch(user(data))

    3.dispatch 函数: type dispatch = (a: Action) => Action

    dispatching function 是一个用于触发 action 的函数,action 是改变 State 的唯一途径,但是它只描述了一个行为,而 dipatch 可以看作是触发这个行为的方式,而 Reducer 则是描述如何改变数据的。
    在 dva 中,connect Model 的组件通过 props 可以访问到 dispatch,可以调用 Model 中的 Reducer 或者 Effects,常见的形式如:

    dispatch({
      type: 'user/add', // 如果在 model 外调用,需要添加 namespace
      payload: {}, // 需要传递的信息
    });

    4.reducer:   type Reducer<S, A> = (state: S, action: A) => S

    Reducer(也称为 reducing function)函数接受两个参数:之前已经累积运算的结果和当前要被累积的值,返回的是一个新的累积结果。该函数把一个集合归并成一个单值。

    //state的值
    const initState = {
    	users:{},//用户信息
    }
    
    //1 reducer 函数
    export function chat(state=initState, action){
    	switch(action.type){
    		case USER_LIST:
    			return {...state,users:action.payload.users}
    		default:
    		 	return state
    	}
    }

    Reducer 的概念来自于是函数式编程,很多语言中都有 reduce API。如在 javascript 中: 

    [{x:1},{y:2},{z:3}].reduce(function(prev, next){
        return Object.assign(prev, next);
    })
    //return {x:1, y:2, z:3}

    在 dva 中,reducers 聚合积累的结果是当前 model 的 state 对象。通过 actions 中传入的值,与当前 reducers 中的值进行运算获得新的值(也就是新的 state)。需要注意的是 Reducer 必须是纯函数,所以同样的输入必然得到同样的输出,它们不应该产生任何副作用。并且,每一次的计算都应该使用immutable data,这种特性简单理解就是每次操作都是返回一个全新的数据(独立,纯净),所以热重载和时间旅行这些功能才能够使用。 

    5.Effect

    用于处理异步操作和业务逻辑,不直接修改 state,简单的来说,就是获取从服务端获取数据,并且发起一个 action 交给 reducer 的地方。基于 Redux-saga 实现。Effect 指的是副作用。根据函数式编程,计算以外的操作都属于 Effect,典型的就是 I/O 操作、数据库读写。

    常见的操作:

    1.put
    用于触发 action 。
    yield put({ type: 'todos/add', payload: 'Learn Dva' });
     
    2.call
    用于调用异步逻辑,支持 promise 。
    const result = yield call(fetch, '/todos');
     
    3.select
    用于从 state 里获取数据。
    const todos = yield select(state => state.todos);
    

    简单的理解Redux-Saga

    6.subscription

    subscription 是订阅,用于订阅一个数据源,然后根据需要 dispatch 相应的 action。在 app.start() 时被执行,数据源可以是当前的时间、当前页面的url、服务器的 websocket 连接、history 路由变化等等。

    一般格式是:

    subscriptions: {
        setup({ dispatch, history }) { 
    		
        },
    },

    异步数据初始化
    比如:当用户进入 /users 页面时,触发 action users/fetch 加载用户数据。 

    app.model({
      subscriptions: {
        setup({ dispatch, history }) {
          history.listen(({ pathname }) => {
            if (pathname === '/users') {
              dispatch({
                type: 'users/fetch',
              });
            }
          });
        },
      },
    });

     键盘事件

    import key from 'keymaster';
    ...
    app.model({
      namespace: 'count',
      subscriptions: {
        keyEvent({dispatch}) {
          key('⌘+up, ctrl+up', () => { dispatch({type:'add'}) });
        },
      }
    });

    (3).Router   表示路由配置信息

    项目中的 router.js。

    这里的路由通常指的是前端路由,由于我们的应用现在通常是单页应用,所以需要前端代码来控制路由逻辑,通过浏览器提供的 History API 可以监听浏览器url的变化,从而控制路由相关操作。

    Route Component 表示 Router 里匹配路径的 Component,通常会绑定 model 的数据

    import { connect } from 'dva';
    
    function App() {
      return <div>App</div>;
    }
    
    function mapStateToProps(state) {
      return { todos: state.todos };
    }
    
    export default connect(mapStateToProps)(App);
    

    三.Dva API

    官网.

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

    opts 包含:
        history:指定给路由用的 history,默认是 hashHistory      关于react-router中的hashHistorybrowserHistory的区别大家可以看:react-router
        initialState:指定初始数据,优先级高于 model 中的 state,默认是 {},但是基本上都在modal里面设置相应的state。
    如果要配置 history 为 browserHistory,可以这样:

    import createHistory from 'history/createBrowserHistory';
    const app = dva({
      history: createHistory(),
    });

    另外,出于易用性的考虑,opts 里也可以配所有的 hooks ,下面包含全部的可配属性:

    const app = dva({
      history,
      initialState,
      onError,
      onAction,
      onStateChange,
      onReducer,
      onEffect,
      onHmr,
      extraReducers,
      extraEnhancers,
    });

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

    这里最常见的就是dva-loading插件的配置,就是引入第三方插件的时候使用

    import createLoading from 'dva-loading';
    ...
    app.use(createLoading(opts));

    3.app.model()

    model 是 dva 中最重要的概念。 这个是你数据逻辑处理,数据流动的地方。

    (1).5个属性

    A.namespace
    model 的命名空间,同时也是他在全局 state 上的属性,只能用字符串,不支持通过 . 的方式创建多层命名空间。
    B.state
    初始值,优先级低于传给 dva() 的 opts.initialState。

    const app = dva({
      initialState: { count: 1 },
    });
    app.model({
      namespace: 'count',
      state: 0,
    });
    此时,在 app.start() 后 state.count 为 1 。

    同上面
    C.reducer
    D.Effect
    E.subscription

    总结:

    Model 对象的属性

    1. namespace: 当前 Model 的名称。整个应用的 State,由多个小的 Model 的 State 以 namespace 为 key 合成
    2. state: 该 Model 当前的状态。数据保存在这里,直接决定了视图层的输出
    3. reducers: Action 处理器,处理同步动作,用来算出最新的 State
    4. effects:Action 处理器,处理异步动作

    4.app.unmodel(namespace)   取消 model 注册

    5.app.router(({ history, app }) => RouterConfig)
    注册路由表,我们做路由跳转的地方。一般是这样写:

    import { Router, Route } from 'dva/router';
    app.router(({ history }) => {
      return (
        <Router history={history}>
          <Route path="/" component={App} />
        <Router>
      );
    });

    推荐把路由信息抽成一个单独的文件,这样结合 babel-plugin-dva-hmr 可实现路由和组件的热加载。

    比如:app.router(require('./router'));

    但是如果你的项目特别的庞大,我们就要考虑到相应的性能的问题,就要router按需加载的写法。 

    6.app.start(selector)

    启动应用,项目跑起来
    app.start('#root');

    四.安装插件

    1.使用 antd

    A.antd 和 babel-plugin-import 。babel-plugin-import 是用来按需加载 antd 的脚本和样式的,

    npm install antd babel-plugin-import --save

    B.编辑 .webpackrc,使 babel-plugin-import 插件生效。

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

    2.安装antd-mobile

    npm install antd-mobile --save 

    直接引用css就好:import 'antd-mobile/dist/antd-mobile.css'

    按需加载:

    安装babel-plugin-import:   cnpm install babel-plugin-import --save

    找到根目录下的.webpackrc文件,并在该文件中添加以下代码

    {
    	"extraBabelPlugins": [
    	    ["import", { "libraryName": "antd-mobile", "style": "css" }]
    	]
    }

    五.操作

    官网:操作         上手快速

    一.路由定义:router.js
    	import React from 'react';
    	import { Router, Route, Switch } from 'dva/router';
    	import User from './routes/user';
    
    	function RouterConfig({ history }) {
    		return (
    			<Router history={history}>
    			  	<Switch>
    			    	<Route path="/user" exact component={User} />
    			  	</Switch>
    			</Router>
    		);
    	}
    
    	export default RouterConfig;
    	浏览器访问:http://localhost:8000/#/user
    
    
    二.定义 model/user:设置state的值
    
       export default {
    	  	namespace: 'user',
    	  	state: {
    		    list: [],
    		    total: null,
    		    loading: false, // 控制加载状态
    			current: null, // 当前分页信息
    			currentItem: {}, // 当前操作的用户对象
    		},
    	}
    
    三.页面组件定义:
    1. routes/user
    	import React from 'react';
    	import { connect } from 'dva';
    	import styles from './user.css';
    	import UserList from '../components/users/userList';
    	import UserSearch from '../components/users/userSearch';
    	import UserModal from '../components/users/userModal';
    
    	function User() {
    		const userSearchProps = {};
    		const userListProps = {};
    		const userModalProps = {};
    
    		return (
    			<div className={styles.normal}>
    			   {/* 用户筛选搜索框 */}
    				<UserSearch {...userSearchProps} />
    				{/* 用户信息展示列表 */ }
    				<UserList {...userListProps} />
    				{/* 添加用户 & 修改用户弹出的浮层 */ }
    				<UserModal {...userModalProps} />
    			</div>
    		);
    	}
    
    
    	export default connect()(User);
    
    2.完成component页面:userList  userSearch userModal
    	import React, { PropTypes } from 'react';
    	import { Table, message, Popconfirm } from 'antd';
    
    	const UserList = ({total,current,loading,dataSource}) => {
    		const columns=[{}];
    		const pagination = {};
    		return (
    			<div>
    		      	<Table  columns={columns}
    			        dataSource={dataSource}
    			        loading={loading}
    			        rowKey={record => record.id}
    			        pagination={pagination} />
    		    </div>
    		);
    	}
    
    	export default userList;
    
    
    注意:routes可以简单的理解是页面组件   components可以理解是模块组件, 
     routes里面引用多个components的组件
    
    
    四.更新state:—》model/user里面完成 reducers  计算新的state
    
    	export default {
    	  	namespace: 'user',
    		reducers: {
    		     // 使用静态数据返回
    		    querySuccess(state){
    		      	const mock = {
    			        total: 3,
    			        current: 1,
    			        loading: false,
    			        list: [
    						{
    							id: 1,
    							name: '张三',
    							age: 23,
    							address: '成都',
    						},
    			        ]
    			    };
    		      	return {...state, ...mock, loading: false};
    		    },
    		    createSuccess(){},
    	    	deleteSuccess(){},
    	    	updateSuccess(){},
    		}
    	}
    
    五.关联Model数据:把model中计算的新state的值  关联到组件 
        =》返回routes/user 
    
    	import { connect } from 'dva';
    	function User({ location, dispatch, user }) {
    		//Model中state 赋值到 { loading, list, total, current,currentItem, modalVisible, modalType}
    		const {
    		    loading, list, total, current,
    		    currentItem, modalVisible, modalType
    		} = user;
    
    		const userListProps = {
    			dataSource: list,
    			total,
    			loading,
    			current,
    		};		
    		const userSearchProps = {};
    		const userModalProps = {};
    
    		return (
    			<div className={styles.normal}>
    			   {/* 用户筛选搜索框 */}
    				<UserSearch {...userSearchProps} />
    				{/* 用户信息展示列表 */ }
    				<UserList {...userListProps} />
    				{/* 添加用户 & 修改用户弹出的浮层 */ }
    				<UserModal {...userModalProps} />
    			</div>
    		);
    	}
        //指定订阅数据,这里关联了model中 user
    	function mapStateToProps({ user }) {
    	  return {user};
    	}
    	//建立数据关联关系
    	const User = connect(mapStateToProps)(User);
    	export default  User;
    
    注意:1.记得 index.js 入口文件处要要关联model :app.model(require('./models/user')); 否则获取不到数据
    
    2.数据关联以后,就可以通过 props 访问到 model 的数据了,
    而 UserList 展示组件的数据,也是 routes/user 组件 通过 props 传递的过来的。
    
    六.数据关联后,就要更新reducer的数据 :model/user
    调用 reducers呢,就是需要发起一个 action。  这边可以使用Subscription 订阅 监听路由的改变
    subscriptions: {
    	    setup({ dispatch, history }) {
    		    history.listen(location => {
    		        if (location.pathname === '/user') {
    		          	dispatch({
    		            	type: 'querySuccess',
    		            	payload: {}
    		          	});
    		        }
    		    });
    	    },
      	},
    
    
    七.异步处理:返回model  effects: {...}的操作
    
        	import { hashHistory } from 'dva/router';
    	import { query } from '../services/user';
    	export default {
    		namespace: 'user',
    		reducers: {
    			showLoading(state, action){
    				return { ...state, loading: true };
    			},// 控制加载状态的 reducer
    			showModal(){}, // 控制 Modal 显示状态的 reducer
    			hideModal(){},
    			// 使用服务器数据返回
    			querySuccess(state, action){
    			  return {...state, ...action.payload, loading: false};
    			},
    		},
    		effects: {
    			*query({ payload }, { select, call, put }) {
    				yield put({ type: 'showLoading' });
    				const { data } = yield call(query);
    				if (data) {
    					yield put({
    						type: 'querySuccess',
    						payload: {
    							list: data.data,
    							total: data.page.total,
    							current: data.page.current
    						}
    					});
    				}
    			},
    		}
    	}
    
    八.后端数据请求
    1.代理 
     在.webpackrc 文件里面加
    "proxy": {
    	  	"/api": {
    	    	"target": "http://localhost:8080",
    	    	"changeOrigin": true,
    	    	"pathRewrite": { 
    	    		"^/api" : "" 
    	    	}
    	  	}
    	},
    
    或是  安装  roadhorg    在.roadhogrc.js里面做代理
    
    2.在services/user 做交互   
    
    	/*与后台系统的交互)
    	request 是我们封装的一个网络请求库
    	qs是一个npm仓库所管理的包  对请求返回的数据进行处理
    */
    	import request from '../utils/request';
    	import qs from 'qs';
    
    	export async function query(params) {
    	  return request(`/api/user?${qs.stringify(params)}`);
    	}

    执行上面8步骤就差不多

    六.补充

    (一).dva 2.0中如何使用代码进行路由跳转

    react-router@4.0 让路由变得更简单,最大特点就是可以路由嵌套。由于 dva 将react-router-domreact-router-redux都封装到了 dva/router 中,在使用 react-router@4.0 和 redux 里面的东西时只需引入 dva/router 这个包即可。react-router@4.0 文档 API

    dva 中使用 router4.0

    router.js:

    import React from 'react';
    import { Router, Route, Switch,routerRedux } from 'dva/router';
    import BasicLayout from '../layouts/BasicLayout'
    
    
    const { ConnectedRouter } = routerRedux;
    function RouterConfig({ history }) {
    	return (
    		<ConnectedRouter history={history}>
            	<Route path="/" component={BasicLayout} />
          	</ConnectedRouter>
    	);
    }
    
    export default RouterConfig;
    

    Route 为 react-router-dom 内的标签
    ConnectedRouter 为 react-router-redux 内的对象 routerRedux 的标签,作用相当于 react-router-dom 中的 BrowserRouter 标签,作用为连接 redux 使用
     

    1.路由跳转

    引入 dva/router,使用 routerReux 对象的 push 方法控制,值为要跳转的路由地址,与根目录下 router.js 中配置的路由地址是相同的。routerReux 就是上面 dva/router 第二个导出的 react-router-redux 包对象。

    此处示例为跳转到 /user 路由。

    // models > app.js
    import { routerRedux } from 'dva/router';
    
    export default {
      // ...
      effects: {
          // 路由跳转
          * redirect ({ payload }, { put }) {
            yield put(routerRedux.push('/user'));
          },
      }
      // ...
    }
    

    2.携带参数
    有时路由的跳转还需要携带参数。

    3.传参:
    routerRedux.push 方法的第二个参数填写参数对象。此处示例表示跳转到 /user 路由,并携带参数 {name: 'dkvirus', age: 20}。

    // models > app.js
    import { routerRedux } from 'dva/router';
    
    export default {
      // ...
      effects: {
          // 路由跳转
          * redirect ({ payload }, { put }) {
            yield put(routerRedux.push('/user', {name: 'dkvirus', age: 20}));
          },
      }
      // ...
    }
    

    接收参数: 

    // models > user.js
    export default {
      subscriptions: {
        /**
         * 监听浏览器地址,当跳转到 /user 时进入该方法
         * @param dispatch 触发器,用于触发 effects 中的 query 方法
         * @param history 浏览器历史记录,主要用到它的 location 属性以获取地址栏地址
         */
        setup ({ dispatch, history }) {
          history.listen((location) => {
            console.log('location is: %o', location);
            console.log('重定向接收参数:%o', location.state)
            // 调用 effects 属性中的 query 方法,并将 location.state 作为参数传递 
            dispatch({
              type: 'query',
              payload: location.state,
            })
          });
        },
      },
      effects: {
        *query ({ payload }, { call, put }) {
           console.log('payload is: %o', payload);
        }
      }
      // ...
    }
    

    在 user.js 中 subscriptions 属性会监听路由。当 app.js 中通过代码跳转到 /user 路由,models>user.js>subscriptions 属性中的 setup 方法会被触发,location 记录着相关信息。打印如下:

    location is: Object
        hash: ""
        key: "kss7as"
        pathname: "/user"
        search: ""
        state: {name: "bob", age: 21}
    重定向接收参数:Object
        age:21
        name:"bob"

     可以看到 location.state 就是传递过来的参数。在 subscriptions 中可以使用 dispatch 触发 effects 中的方法同时传递参数。

    (三). 解决组件动态加载问题的 util 方法

    import dynamic from 'dva/dynamic';

    opts 包含:

    • app: dva 实例,加载 models 时需要
    • models: 返回 Promise 数组的函数,Promise 返回 dva model
    • component:返回 Promise 的函数,Promise 返回 React Component

    使用如下:

    mport dynamic from 'dva/dynamic'
    
    {
    routeArr.map((item, key) => {
    	return <Route key={key} exact path={item.path} component={dynamic({ //保证路由的唯一性 exact key
    		app,
    		model: item.models,
    		component: item.component,
    	})} />
    })
    
    }

    (四).React.Component与React.PureComponent的区别


    React15.3中新加了一个 PureComponent 类,顾名思义, pure 是纯的意思, PureComponent 也就是纯组件,取代其前身 PureRenderMixin , PureComponent 是优化 React 应用程序最重要的方法之一,易于实施,只要把继承类从 Component 换成 PureComponent 即可,可以减少不必要的 render操作的次数,从而提高性能,而且可以少写 shouldComponentUpdate 函数,节省了点代码。

        React.PureComponent 与 React.Component 几乎完全相同,但 React.PureComponent 通过prop和state的浅对比来实现 shouldComponentUpate()
    如果React组件的 render() 函数在给定相同的props和state下渲染为相同的结果,在某些场景下你可以使用 React.PureComponent 来提升性能

    七.参考

    1. Dva-React 应用框架在蚂蚁金服的实践
    2.   dva.js 知识导图     
    3. redux-saga   
    4.  UMI       
    5. dva理论到实践——帮你扫清dva的知识盲点      
    6. History API   
    7. redux docs 中文
    8. roadhog介绍
    9. 基于dva-cli&antd的react项目实战
    10. 10分钟 让你dva从入门到精通
    11. 上手快速
    12. async 函数的含义和用法
    13. 基于 dva 创建 antd-mobile 的项目

    八.案例

    1.后台管理系统

    展开全文
  • react前端框架dva(一)

    2018-03-22 11:57:37
    react前端框架dva 最近正在看dva框架,发现这是一个很好应用于react框架。 众所周知,react的学习成本是较高的,而antd推出的这款框架,大大的降低了react的学习成本。dva简化了 react-redux 的部署的文件复杂度...

    react前端框架dva

    最近正在看dva框架,发现这是一个很好应用于react的框架。
    众所周知,react的学习成本是较高的,而antd推出的这款框架,大大的降低了react的学习成本。dva简化了 react-redux 的部署的文件复杂度。使其清晰易懂。相信熟悉redux 的人,并不需要了解太多知识就能很快的上手。


    Dva的安装

    首先需要安装的是 dva-cli 。dva-cli 是 dva 的命令行工具,包含 init、new、generate 等功能,目前最重要的功能是可以快速生成项目以及你所需要的代码片段。
    npm install -g dva-cli
    安装完成后,可以通过 dva -v 查看版本,以及 dva -h 查看帮助信息。
    创建新应用
    安装完 dva-cli 后,我们用他来创建一个新应用,取名 myDome 。
    dva new myDome –demo
    注意: –demo 用于创建简单的 demo 级项目,正常项目初始化不加要这个参数。
    然后进入项目目录,并启动。
    cd myDome
    npm start
    几秒之后,会看到这样的输出:
    proxy: listened on 8989 livereload: listening on 35729 :package: 173/173 build modules webpack: bundle build is now finished.
    (如需关闭 server,请按 Ctrl-C.)

    在浏览器里打开 http://localhost:8989/ ,正常情况下,你会看到一个 “Hello Dva” 页面。

    开始进入我们正式的dva学习吧,点击进入:react前端框架dva(二)

    展开全文
  • 应用 dva 框架搭建平台,dva 是基于 redux 最佳实践实现的framework,简化使用 redux 和redux-saga 时很多繁杂的操作。 dva 文档比较健全,可以访问...

    Dva整体理解


    前言

    README.md

    欢迎阅读,本文档将带你了解dva的整体思路。

    介绍

    应用 dva 框架搭建平台,dva 是基于 redux 最佳实践实现的framework,简化使用 redux 和redux-saga 时很多繁杂的操作。 dva 文档比较健全,可以访问https://github.com/dvajs/dva-docs/blob/master/v1/zh-cn/tutorial/01-%E6%A6%82%E8%A6%81.md查看更多内容,其中会介绍整个框架的开发思路。

     

    关于dva

    dva 是基于现有应用架构 (redux + react-router + redux-saga 等)的一层轻量封装;

    数据流向: 数据的改变发生通常是通过:

    • 用户交互行为(用户点击按钮等)
    • 浏览器行为(如路由跳转等)触发的

    当此类行为会改变数据的时候可以通过 dispatch 发起一个 action ,如果是同步行为会直接通过Reducers 改变 State ,如果是异步行为(副作用)会先触发 Effects 然后流向 Reducers 最终改变State 。 所以在 dva 中,数据流向非常清晰简明,如上图。

     

    关于Redux-saga

    Redus-saga 是一个 redux 的中间件,主要用来简便而优雅的处理 redux 应用里的副作用(side effect相对于pure function这类概念而言的)。它之所以可以做到这一点主要是使用了ES6 里的一个语法: Generator 。使用 Generator 可以像写同步的代码一样编写异步代码,这样更加容易测试。

     

    准备

    本文档第一部分将带领大家获得项目源码。首先需要的是将 gitlad 上的远程项目仓库克隆到本地;

    $ git clone http://gitlab.xxxx.com/xx/xxx.git
    

    克隆完成后,本地目标文件夹里就会出现目标远程仓库的文件;

    目录

    本文档的第一部分将带领大家了解dva项目 目录结构。在下载源码之后,我们可以看到,整个系统的构建以及管理。目录如下:

    工程结构如下:

    ● xxxx
     ○ public
     ○ src                          #项目源码目录
       ■ assets                     #静态资源
       ■ common                     #路由资源
       ■ components                 #项目组件
       ■ layouts                    #项目布局组件
       ■ models                     #数据模型
       ■ routes                     #路由组件(页面维度)
       ■ services                   #数据接口
       ■ utils                      #工具函数
       ■ index.js                   #入口文件
       ■ router.js                  #路由配置
    

    开始

    增加一个新功能

    在上面的介绍中中我们已经对 dva 有了一定的认识,接下来我们会一起添加一个较为完善的功能,在实现的过程中,我们逐步完成以下内容:

    1. 添加顶层路由数据
    2. 添加路由
    3. 添加布局
    4. 定义service
    5. 设计Model
    6. 组件设计

    添加顶层路由数据

    第一步,我们会按照功能在 ./common/nav.js 添加路由数据:(部分代码)

    // common/nav.js
    {
      name: 'xxx',
      path: 'aaa',
      icon: 'global',
      page: 'ppp',
      grayscale: true,
      children: [
      {
         component: dynamicWrapper(app, ['aaa'], () =>  
                    import('../routes/aaa')) 
      },
      //......
      ],
    }
    

     

    定义路由

    路由决定进入 url 渲染哪些 Component 。 history 默认是 hashHistory;

    // router.js
    return (
      <LocaleProvider locale={zhCN}>
        <Router history={history}>
          <Switch>
            <Route path="/login" render={props => <XxxLayout {...props}   
            {...passProps} />} />
            <Route path="/sss" component={sss} />
            <Redirect exact from="/" to="/index" />
        </Switch>
       </Router>
      </LocaleProvider>
    );
    

     

    设计布局

    路由添加完成后,设计布局,这些布局内容被抽象成组件,包含一些布局样式,用于组合其它组件搭建成页面。
    本质上还是一种组件,将布局样式抽象成组件,能够保持子组件和父组件的独立性,不用在其中关联到布局信息。

    注:若添加的是二级功能,不需要添加新的布局

     

    定义service

    接下来就是将请求相关抽离出来,单独放到 /services/ 中,进行统一维护管理;
    如:

    // services/xxx.js
    export async function xxx(params) {
    return     request(`xxx.json`, {
        method: 'GET',
    });
    }
    
    

    设计Model

    在完成项目基本的前期以后,我们将要开始设计 model ,在设计 model 之前,我们需要回顾一下项目的功能;
    我们可以看出,这部分功能基本是围绕 以用户数据为基础 的操作,其中包含:

    • 用户信息的展示
    • 用户信息的操作

    无论是多复杂的项目也基本上是围绕着数据的展示和操作,复杂一点的是组合了很多数据,有关联关系,只要分解开来, model 的结构层次依旧会很清晰,便于维护。
    所以抽离 model 的原则就是抽离数据模型。

    export default { 
    namespace: 'xxx',
    state: {
        helps: [],
        task: {},
        data: {
          list: [],
          pagination: {
             current: 1,
          },
        },
    },
      effects: {
        * auth() {},
        * list() {};
        * detail() {},
        * xxList() {};
        * xxOperation() {},
      },
      reducers: {
        save(){}
      },
    };
    

    model 包含了关于操作的 state ,处理异步的 effect(dispatch action) ,以及修改 state 的reducers

    视图设计

    数据设计完成后,我们着手新增组件的编写,在 ./routes 中添加新增组件,根据 @connect 包裹组件的 props 进行操作的编写:

    @connect(({ a1, a2 }) => ({
      a1,
      a2: ......,
    }))
    export default class xxx extends PureComponent {
      state = {
        page: 1,
        type: 1,
        //.....
      }
    
      componentDidMount() {
        const { dispatch } = this.props;
        dispatch({
          type: 'xx/auth',
        });
        dispatch({
          type: 'xx/list',
          payload: {
            page: 1,
            type: 1,
          },
        });
      }
    

     

    组件设计

    在确定了大体的设计方法以后,让我们来看看如何设计 dva 中的 React 组件。
    React 应用是由一个个独立的 Component 组成的,我们在拆分 Component 的过程中要尽量让每个Component 专注做自己的事。

    对组件分类,主要有两个好处:

    • 让项目的数据处理更加集中;
    • 让组件高内聚低耦合,更加聚焦;

     

    注:本平台使用 ant design 进行组件的设计,避免样式表的复杂编写;

     

    写在最后

    dva将所有与数据操作相关的逻辑集中放在一个地方处理和维护,在数据跟业务状态交互比较紧密的场景下,会使我们的代码更加清晰可控。

    对于一个大型管理系统,由于要进行大量的数据操作,在设计model时将不同类型的业务需求数据操作分开处理,便于维护。

    项目的开发流程一般是从设计 model state 开始进行抽象数据,完成 component 后,将组件和model 建立关联,通过 dispatch 一个 action ,在 reducer 中更新数据完成数据同步处理;当需要从服务器获取数据时,通过 Effects 数据异步处理,然后调用 Reducer 更新全局 state 。是一个单向的数据流动过程。解决了 redux 中代码分散和重写问题,总之,Dva:Build redux application easier and better。

    官方地址
    Ant Design
    Redux-saga 中文文档
    dva-knowledgemap
    dva: react application arch in ant financial

    展开全文
  • react前端框架dva(四)

    2018-03-22 11:40:52
    dva.js 知识导图不知大家学 reactdva 时会不会有这样的疑惑:es6 特性那么多,我需要全部学会吗?react component 有 3 种写法,我需要全部学会吗?reducer 的增删改应该怎么写?怎么做全局/局部的错误处理?怎么发...

    dva.js 知识导图

    不知大家学 react 或 dva 时会不会有这样的疑惑:

    • es6 特性那么多,我需要全部学会吗?
    • react component 有 3 种写法,我需要全部学会吗?
    • reducer 的增删改应该怎么写?
    • 怎么做全局/局部的错误处理?
    • 怎么发异步请求?
    • 怎么处理复杂的异步业务逻辑?
    • 怎么配置路由?
    • ...

    这篇文档梳理了基于 dva-cli 使用 dva 的最小知识集,让你可以用最少的时间掌握创建类似 dva-hackernews 的全部知识,并且不需要掌握额外的冗余知识。

    JavaScript 语言

    变量声明

    const 和 let

    不要用 var,而是用 const 和 let,分别表示常量和变量。不同于 var 的函数作用域,const 和 let 都是块级作用域。

    const DELAY = 1000;
    
    let count = 0;
    count = count + 1;

    模板字符串

    模板字符串提供了另一种做字符串组合的方法。

    const user = 'world';
    console.log(`hello ${user}`);  // hello world
    
    // 多行
    const content = `
      Hello ${firstName},
      Thanks for ordering ${qty} tickets to ${event}.
    `;

    默认参数

    function logActivity(activity = 'skiing') {
      console.log(activity);
    }
    
    logActivity();  // skiing

    箭头函数

    函数的快捷写法,不需要通过 function 关键字创建函数,并且还可以省略 return 关键字。

    同时,箭头函数还会继承当前上下文的 this 关键字。

    比如:

    [1, 2, 3].map(x => x + 1);  // [2, 3, 4]

    等同于:

    [1, 2, 3].map((function(x) {
      return x + 1;
    }).bind(this));

    模块的 Import 和 Export

    import 用于引入模块,export 用于导出模块。

    比如:

    // 引入全部
    import dva from 'dva';
    
    // 引入部分
    import { connect } from 'dva';
    import { Link, Route } from 'dva/router';
    
    // 引入全部并作为 github 对象
    import * as github from './services/github';
    
    // 导出默认
    export default App;
    // 部分导出,需 import { App } from './file'; 引入
    export class App extend Component {};

    ES6 对象和数组

    析构赋值

    析构赋值让我们从 Object 或 Array 里取部分数据存为变量。

    // 对象
    const user = { name: 'guanguan', age: 2 };
    const { name, age } = user;
    console.log(`${name} : ${age}`);  // guanguan : 2
    
    // 数组
    const arr = [1, 2];
    const [foo, bar] = arr;
    console.log(foo);  // 1

    我们也可以析构传入的函数参数。

    const add = (state, { payload }) => {
      return state.concat(payload);
    };

    析构时还可以配 alias,让代码更具有语义。

    const add = (state, { payload: todo }) => {
      return state.concat(todo);
    };

    对象字面量改进

    这是析构的反向操作,用于重新组织一个 Object 。

    const name = 'duoduo';
    const age = 8;
    
    const user = { name, age };  // { name: 'duoduo', age: 8 }

    定义对象方法时,还可以省去 function 关键字。

    app.model({
      reducers: {
        add() {}  // 等同于 add: function() {}
      },
      effects: {
        *addRemote() {}  // 等同于 addRemote: function*() {}
      },
    });

    Spread Operator

    Spread Operator 即 3 个点 ...,有几种不同的使用方法。

    可用于组装数组。

    const todos = ['Learn dva'];
    [...todos, 'Learn antd'];  // ['Learn dva', 'Learn antd']

    也可用于获取数组的部分项。

    const arr = ['a', 'b', 'c'];
    const [first, ...rest] = arr;
    rest;  // ['b', 'c']
    
    // With ignore
    const [first, , ...rest] = arr;
    rest;  // ['c']

    还可收集函数参数为数组。

    function directions(first, ...rest) {
      console.log(rest);
    }
    directions('a', 'b', 'c');  // ['b', 'c'];

    代替 apply。

    function foo(x, y, z) {}
    const args = [1,2,3];
    
    // 下面两句效果相同
    foo.apply(null, args);
    foo(...args);

    对于 Object 而言,用于组合成新的 Object 。(ES2017 stage-2 proposal)

    const foo = {
      a: 1,
      b: 2,
    };
    const bar = {
      b: 3,
      c: 2,
    };
    const d = 4;
    
    const ret = { ...foo, ...bar, d };  // { a:1, b:3, c:2, d:4 }

    此外,在 JSX 中 Spread Operator 还可用于扩展 props,详见 Spread Attributes

    Promises

    Promise 用于更优雅地处理异步请求。比如发起异步请求:

    fetch('/api/todos')
      .then(res => res.json())
      .then(data => ({ data }))
      .catch(err => ({ err }));

    定义 Promise 。

    const delay = (timeout) => {
      return new Promise(resolve => {
        setTimeout(resolve, timeout);
      });
    };
    
    delay(1000).then(_ => {
      console.log('executed');
    });

    Generators

    dva 的 effects 是通过 generator 组织的。Generator 返回的是迭代器,通过 yield 关键字实现暂停功能。

    这是一个典型的 dva effect,通过 yield 把异步逻辑通过同步的方式组织起来。

    app.model({
      namespace: 'todos',
      effects: {
        *addRemote({ payload: todo }, { put, call }) {
          yield call(addTodo, todo);
          yield put({ type: 'add', payload: todo });
        },
      },
    });

    React Component

    Stateless Functional Components

    React Component 有 3 种定义方式,分别是 React.createClassclass 和 Stateless Functional Component。推荐尽量使用最后一种,保持简洁和无状态。这是函数,不是 Object,没有 this 作用域,是 pure function。

    比如定义 App Component 。

    function App(props) {
      function handleClick() {
        props.dispatch({ type: 'app/create' });
      }
      return <div onClick={handleClick}>${props.name}</div>
    }

    等同于:

    class App extends React.Component {
      handleClick() {
        this.props.dispatch({ type: 'app/create' });
      }
      render() {
        return <div onClick={this.handleClick.bind(this)}>${this.props.name}</div>
      }
    }

    JSX

    Component 嵌套

    类似 HTML,JSX 里可以给组件添加子组件。

    <App>
      <Header />
      <MainContent />
      <Footer />
    </App>

    className

    class 是保留词,所以添加样式时,需用 className 代替 class 。

    <h1 className="fancy">Hello dva</h1>

    JavaScript 表达式

    JavaScript 表达式需要用 {} 括起来,会执行并返回结果。

    比如:

    <h1>{ this.props.title }</h1>

    Mapping Arrays to JSX

    可以把数组映射为 JSX 元素列表。

    <ul>
      { this.props.todos.map((todo, i) => <li key={i}>{todo}</li>) }
    </ul>

    注释

    尽量别用 // 做单行注释。

    <h1>
      {/* multiline comment */}
      {/*
        multi
        line
        comment
        */}
      {
        // single line
      }
      Hello
    </h1>

    Spread Attributes

    这是 JSX 从 ECMAScript6 借鉴过来的很有用的特性,用于扩充组件 props 。

    比如:

    const attrs = {
      href: 'http://example.org',
      target: '_blank',
    };
    <a {...attrs}>Hello</a>

    等同于

    const attrs = {
      href: 'http://example.org',
      target: '_blank',
    };
    <a href={attrs.href} target={attrs.target}>Hello</a>

    Props

    数据处理在 React 中是非常重要的概念之一,分别可以通过 props, state 和 context 来处理数据。而在 dva 应用里,你只需关心 props 。

    propTypes

    JavaScript 是弱类型语言,所以请尽量声明 propTypes 对 props 进行校验,以减少不必要的问题。

    function App(props) {
      return <div>{props.name}</div>;
    }
    App.propTypes = {
      name: React.PropTypes.string.isRequired,
    };

    内置的 prop type 有:

    • PropTypes.array
    • PropTypes.bool
    • PropTypes.func
    • PropTypes.number
    • PropTypes.object
    • PropTypes.string

    往下传数据

    往上传数据

    CSS Modules

    理解 CSS Modules

    一张图理解 CSS Modules 的工作原理:

    button class 在构建之后会被重命名为 ProductList_button_1FU0u 。button 是 local name,而 ProductList_button_1FU0u是 global name 。你可以用简短的描述性名字,而不需要关心命名冲突问题。

    然后你要做的全部事情就是在 css/less 文件里写 .button {...},并在组件里通过 styles.button 来引用他。

    定义全局 CSS

    CSS Modules 默认是局部作用域的,想要声明一个全局规则,可用 :global 语法。

    比如:

    .title {
      color: red;
    }
    :global(.title) {
      color: green;
    }

    然后在引用的时候:

    <App className={styles.title} /> // red
    <App className="title" />        // green

    classnames Package

    在一些复杂的场景中,一个元素可能对应多个 className,而每个 className 又基于一些条件来决定是否出现。这时,classnames 这个库就非常有用。

    import classnames from 'classnames';
    const App = (props) => {
      const cls = classnames({
        btn: true,
        btnLarge: props.type === 'submit',
        btnSmall: props.type === 'edit',
      });
      return <div className={ cls } />;
    }

    这样,传入不同的 type 给 App 组件,就会返回不同的 className 组合:

    <App type="submit" /> // btn btnLarge
    <App type="edit" />   // btn btnSmall

    Reducer

    reducer 是一个函数,接受 state 和 action,返回老的或新的 state 。即:(state, action) => state

    增删改

    以 todos 为例。

    app.model({
      namespace: 'todos',
      state: [],
      reducers: {
        add(state, { payload: todo }) {
          return state.concat(todo);
        },
        remove(state, { payload: id }) {
          return state.filter(todo => todo.id !== id);
        },
        update(state, { payload: updatedTodo }) {
          return state.map(todo => {
            if (todo.id === updatedTodo.id) {
              return { ...todo, ...updatedTodo };
            } else {
              return todo;
            }
          });
        },
      },
    };

    嵌套数据的增删改

    建议最多一层嵌套,以保持 state 的扁平化,深层嵌套会让 reducer 很难写和难以维护。

    app.model({
      namespace: 'app',
      state: {
        todos: [],
        loading: false,
      },
      reducers: {
        add(state, { payload: todo }) {
          const todos = state.todos.concat(todo);
          return { ...state, todos };
        },
      },
    });

    下面是深层嵌套的例子,应尽量避免。

    app.model({
      namespace: 'app',
      state: {
        a: {
          b: {
            todos: [],
            loading: false,
          },
        },
      },
      reducers: {
        add(state, { payload: todo }) {
          const todos = state.a.b.todos.concat(todo);
          const b = { ...state.a.b, todos };
          const a = { ...state.a, b };
          return { ...state, a };
        },
      },
    });

    Effect

    示例:

    app.model({
      namespace: 'todos',
      effects: {
        *addRemote({ payload: todo }, { put, call }) {
          yield call(addTodo, todo);
          yield put({ type: 'add', payload: todo });
        },
      },
    });

    Effects

    put

    用于触发 action 。

    yield put({ type: 'todos/add', payload: 'Learn Dva' });

    call

    用于调用异步逻辑,支持 promise 。

    const result = yield call(fetch, '/todos');

    select

    用于从 state 里获取数据。

    const todos = yield select(state => state.todos);

    错误处理

    全局错误处理

    dva 里,effects 和 subscriptions 的抛错全部会走 onError hook,所以可以在 onError 里统一处理错误。

    const app = dva({
      onError(e, dispatch) {
        console.log(e.message);
      },
    });

    然后 effects 里的抛错和 reject 的 promise 就都会被捕获到了。

    本地错误处理

    如果需要对某些 effects 的错误进行特殊处理,需要在 effect 内部加 try catch 。

    app.model({
      effects: {
        *addRemote() {
          try {
            // Your Code Here
          } catch(e) {
            console.log(e.message);
          }
        },
      },
    });

    异步请求

    异步请求基于 whatwg-fetch,API 详见:https://github.com/github/fetch

    GET 和 POST

    import request from '../util/request';
    
    // GET
    request('/api/todos');
    
    // POST
    request('/api/todos', {
      method: 'POST',
      body: JSON.stringify({ a: 1 }),
    });

    统一错误处理

    假如约定后台返回以下格式时,做统一的错误处理。

    {
      status: 'error',
      message: '',
    }

    编辑 utils/request.js,加入以下中间件:

    function parseErrorMessage({ data }) {
      const { status, message } = data;
      if (status === 'error') {
        throw new Error(message);
      }
      return { data };
    }

    然后,这类错误就会走到 onError hook 里。

    Subscription

    subscriptions 是订阅,用于订阅一个数据源,然后根据需要 dispatch 相应的 action。数据源可以是当前的时间、服务器的 websocket 连接、keyboard 输入、geolocation 变化、history 路由变化等等。格式为 ({ dispatch, history }) => unsubscribe 。

    异步数据初始化

    比如:当用户进入 /users 页面时,触发 action users/fetch 加载用户数据。

    app.model({
      subscriptions: {
        setup({ dispatch, history }) {
          history.listen(({ pathname }) => {
            if (pathname === '/users') {
              dispatch({
                type: 'users/fetch',
              });
            }
          });
        },
      },
    });

    path-to-regexp Package

    如果 url 规则比较复杂,比如 /users/:userId/search,那么匹配和 userId 的获取都会比较麻烦。这是推荐用 path-to-regexp简化这部分逻辑。

    import pathToRegexp from 'path-to-regexp';
    
    // in subscription
    const match = pathToRegexp('/users/:userId/search').exec(pathname);
    if (match) {
      const userId = match[1];
      // dispatch action with userId
    }

    Router

    Config with JSX Element (router.js)

    <Route path="/" component={App}>
      <Route path="accounts" component={Accounts}/>
      <Route path="statements" component={Statements}/>
    </Route>

    详见:react-router

    Route Components

    Route Components 是指 ./src/routes/ 目录下的文件,他们是 ./src/router.js 里匹配的 Component。

    通过 connect 绑定数据

    比如:

    import { connect } from 'dva';
    function App() {}
    
    function mapStateToProps(state, ownProps) {
      return {
        users: state.users,
      };
    }
    export default connect(mapStateToProps)(App);

    然后在 App 里就有了 dispatch 和 users 两个属性。

    Injected Props (e.g. location)

    Route Component 会有额外的 props 用以获取路由信息。

    • location
    • params
    • children

    更多详见:react-router

    基于 action 进行页面跳转

    import { routerRedux } from 'dva/router';
    
    // Inside Effects
    yield put(routerRedux.push('/logout'));
    
    // Outside Effects
    dispatch(routerRedux.push('/logout'));
    
    // With query
    routerRedux.push({
      pathname: '/logout',
      query: {
        page: 2,
      },
    });

    除 push(location) 外还有更多方法,详见 react-router-redux

    dva 配置

    Redux Middleware

    比如要添加 redux-logger 中间件:

    import createLogger from 'redux-logger';
    const app = dva({
      onAction: createLogger(),
    });

    注:onAction 支持数组,可同时传入多个中间件。

    history

    切换 history 为 browserHistory

    import { browserHistory } from 'dva/router';
    const app = dva({
      history: browserHistory,
    });

    去除 hashHistory 下的 _k 查询参数

    import { useRouterHistory } from 'dva/router';
    import { createHashHistory } from 'history';
    const app = dva({
      history: useRouterHistory(createHashHistory)({ queryKey: false }),
    });

    展开全文
  • dva版本从1.2.1=>2.1.0(内置react-router2=>react-router4) 其实react升级基本只更新一下包就可以了,主要是react-router升到4有很大的破坏性改动 ###升级总结### 1.react-router4不能嵌套<route>,现...
  • 作者:zhenhua-lee ...来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请...框架: dva是个框架,集成了redux、redux-saga、react-router-redux、react-router 快速初始化: 可以快速实现项目...
  • 里面是两个不同的方法,不同的界面。新人试手作品,仅对入门级有帮助。如果大神想指导不胜感激。
  • reactdva框架的使用

    2019-02-27 11:35:31
    dva框架的中文官网dva中文教程 在介绍dva之前,推荐一款网页编程工具,codesandbox,在线编写工具,前提要网速要快,这样就不用在内存中下载模块了,随用随时下载模块,缺点就是网速慢时界面不好 使用 $ npm ...
  • Min-Admin 基于dva框架 antd的React后台模板
  • ReactDva的学习

    2019-09-11 09:09:29
    一、创建Dva工程 我们前两篇都是用create-react-app创建工程的,但是它只能创建一个最基本的空程序。在前两篇中,我们自己用npm装上了router,redux等依赖包,并自己手动写了很多操作。 Dva将上述一切进行了简化,它...
  • 最近在用react写web项目,领导为了让前端便于维护要求都用react作为开发基础,框架选型不限。在使用 react 的时候或多或少会接触到状态管理,从开始学 react 到现在也挺久了,做一些前端框架选型总结。 dva经朋友...
  • umi官方推荐结合dva使用更配哦,其实他们都是同一位开发者开发的,属于阿里内部开源框架。 1 修改.umirc.js,开启dva支持 // ref: https://umijs.org/config/ export default { plugins: [ // ref: ...
  • Dva 是由阿里架构师 sorrycc 带领 team 完成的一套前端框架,在作者的 github 里是这么描述它的:“dvareact 和 redux 的最佳实践”。现在已经有了自己的官网https://dvajs.com umi 是 dva 作者 sor...
  • dva 是体验技术部开发的 React 应用框架,将上面三个 React 工具库包装在一起,简化了 API,让开发 React 应用更加方便和快捷。 dva = React-Router + Redux + Redux-saga #dva 应用的最简结构 import dva from ...
  • Dva最佳实践一. 门槛React 技术栈二. Hello World三. 一个简单H5功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容...
  • React+DVA开发实践

    2017-05-27 12:09:28
    文档概述本文档在前面章节简单的介绍了React和其相关的一系列技术,最后章节介绍了React+Dva开发的整套过程和基本原理,也就是将一系列框架整合的结果。
  • dva+react 设置启动端口

    2019-12-18 11:28:16
    dva+react 设置启动端口 根据需要,一个电脑会同时运行多个项目,如果不设置默认端口的话,都是8000,那么如果有一个项目正在运行,重新启动一个的话,会提示是否需要重新开启一个,选择yes之后就没有下文了。所以...
  • react前端框架dva(三)

    2018-03-22 13:06:43
    dva/router默认输出 react-router 接口, react-router-redux 的接口通过属性 routerRedux 输出。比如:import { Router, Route, routerRedux } from 'dva/router';dva/fetch异步请求库,输出 isomorphic-fetch...
  • dva + react hooks实战Demo

    2020-06-21 16:16:52
    dva 是一款轻量级的应用框架,是阿里旗下的开源产品.dva是基于redux和redux-saga的数据流方案,然后为了简化开发体验,dva 还额外内置了react-router和fetch.写过原生redux代码的同学应该体会的到,redux里面充斥着大量...
1 2 3 4 5 ... 20
收藏数 1,460
精华内容 584
关键字:

dva react 框架