DvaJS

阅读数:109 评论数:0

跳转到新版页面

分类

html/css/js

正文

Dva是基于redux、redux-saga和react-router的轻量级前端框架及最佳实践沉淀。

https://dvajs.com/guide/concepts.html#%E6%95%B0%E6%8D%AE%E6%B5%81%E5%90%91

数据流向

数据的改变发生通常是通过用户交互行为或者浏览器行为(如路由跳转等)触发的,当此类行为会改变数据的时候可以通过dispatch发起一个action,如果是同步行为会直接通过Reducers改变State,如果是异步行为会先触发Effects然后流向Reducers最终改变State。

Models

前端的代码结构一般分三层:

(1)page负责与用户直接打交道:渲染页面、接受用户的操作输入、侧重于展示型交互性逻辑。

(2)Model负责处理业务逻辑,为Page做数据、状态的读写、变换、暂存等。

(3)Service负责与Http接口对接,进行纯粹的数据读写。

所以Model是前端分层中的腰部力量,承上启下。

1、State

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

2、Action

Action是一个普通javascript对象,它是改变State的唯一途径,无论是从UI事件、网络回调,还是WebSocket等数据所获得的数据,最终都会通过dispatch函数调用一个action,从而改变对应的数据。action必须带有type属性指明具体的行为,其它字段可以自定义。如果要发起一个action需要使用dispatch函数,需要注意的是dispatch是在组件connect Models以后,通过props传入的。

connect 是连接dva和React两个平行世界的关键:

(1)connect让组件获取两样东西,一是model中的数据,二是驱动model改变的方法。

(2)connect本质上只是一个javascript函数,通过@装饰器语法使用,放置在组件定义的上方。

(3)connnect既然是函数,就可以接受入参,第一个入参是最常用的,它需要一个函数,我们习惯给它命名为mapStateToProps,顾明思义就是把dva model中的state通过组件的props注入给组件。通过实现这个函数,我们能实现把dva model的state注入给组件。

3、dispatch函数

在dva中,connect Model的组件通过props可以访问到dispatch,可以调用Model中的Reducer或者Effects,常见的形式如:

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

dispatch函数就是和dva model打交道的唯一途径,dispatch函数接受一个对象作为入参,在概念上我们称为action,唯一强制要包含的是type字段,string类型,用来告诉dva我们想要做什么。

4、Reducer

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

接受两个参数:之前已经累积运算的结果和当前要被累积的值,返回一个新的累积结果。

Reducer的概念来自于函数式编程。

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

每次操作都返回一个全新的数据(独立、纯净),所以热重载和时间旅行这些功能才能够使用。

每个reducer都是一个function,action派发后,通过action.type被唯一匹配到,随后执行函数体逻辑,返回值被dva使用作为新的state,state的改变随后会被connect注入到组件中,触发视图改变。

 

5、Effect

异步操作,也称为副作用。之所以叫副作用是因为同样的输入不一定获得同样的输出。dva为了控制副作用的操作,底层引入了redux-sagas做异步流程控制,将异步转成同步写法。

当action被dispatch之后,会先到达effect处理副作用,然后最终会促使新的action发送出去,这个新的action可能被其他的effect再捕获继续处理,也可能被reducer捕获并结束,无论怎样,最终处理逻辑的终点都将是reducer。

dva中一个典型的effect的写法是:

getData: function* ({ payload }, { call, put }) {
  const data = yield call(SomeService.getEndpointData, payload, 'maybeSomeOtherParams');
  yield put({ type: 'getData_success', payload: data });
}

当这个effect被执行时,入参有两个对象,第一个对象就是匹配这个effect的action对象,因此可以取到约定 的payload这个字段,第二个对象是effect原语集,其中call , put最为常用。入参的两个对象都是在运行时由dva注入进去的。call其实是一个函数,和yield关键字配合使用处理异步逻辑,call第一个参数是个函数,要求返回返回Promise,之后的参数是该函数调用时的入参。yield call调用后就阻塞了,Promise被解析后,得到异步调用的结果,存储到data中,然后程序才能继续里德。put也是一个函数,put和yield配合使用,用来派发一个action。

异步的实质是事件发生促使程序的执行点来回跳转,我们使用callback本质上是描述跳转的一种手段,generator function并没有改变异步的本质,只是改变了描述的方式,使得程序看起来像是同步一样。

generator function定义了流程,并在每次yield节点上报想做的事情,而异步的真正执行逻辑由generator function句柄的持有者代为执行,对应到dva上也是一样,我们通过call向dva描述了想做的事情:请帮我执行这个函数,Promise解析后通知我继续执行,并把Promise的解析值返回给我。

6、Subscription

用于订阅一个数据源,然后根据条件dispatch需要的action,数据源可以是当前的时间、服务器的websocket连接、keyboard输入、geolocation变化、history路由变化等。

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

7、Router

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

dva 实例提供了router方法来控制路由,使用react-router

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

8、Route Components

在dva中,通常需要connect Model的组件都是Route Components,组织在/routes/目录下,而/components/目录下则是纯组件。




相关推荐

dva是一个基于redux和redux-sage的数据流方案, 然后为了简化开发体验, dva还额外内置了react-router和fetch. 参考: <a href="http