-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
243 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
# Concepts | ||
|
||
[以中文版查看此文](./Concepts_zh-CN.md) | ||
|
||
## Data Flow | ||
|
||
<img src="https://zos.alipayobjects.com/rmsportal/PPrerEAKbIoDZYr.png" width="807" /> | ||
|
||
## Models | ||
|
||
### State | ||
|
||
`type State = any` | ||
|
||
The state tree of your models. Usually, the state is a javascript object(Technically it can be any type), which is a immutable data. | ||
|
||
In dva, you can access top state tree data by `_store`. | ||
|
||
```javascript | ||
const app = dva(); | ||
console.log(app._store); // top state | ||
``` | ||
|
||
### Action | ||
|
||
`type AsyncAction = any` | ||
|
||
Just like Redux's Action, in dva, action is a plain object that represents an intention to change the state. Actions are the only way to get data into the store. Any data, whether from UI events, network callbacks, or other sources such as WebSockets needs to eventually be dispatched as actions.action.(ps:dispatch is realized through props by connecting components.) | ||
|
||
```javascript | ||
dispatch({ | ||
type: 'add', | ||
}); | ||
``` | ||
|
||
### dispatch function | ||
|
||
`type dispatch = (a: Action) => Action` | ||
|
||
A dispatching function (or simply dispatch function) is a function that accepts an action or an async action; it then may or may not dispatch one or more actions to the store. | ||
|
||
Dispatching function is a function for triggering action, action is the only way to change state, but it just describes an action. while dispatch can be regarded as a way to trigger this action, and Reducer is to describe how to change state. | ||
|
||
```javascript | ||
dispatch({ | ||
type: 'user/add', // if in model outside, need to add namespace | ||
payload: {}, | ||
}); | ||
``` | ||
|
||
### Reducer | ||
|
||
`type Reducer<S, A> = (state: S, action: A) => S` | ||
|
||
Just like Redux's Reducer, a reducer (also called a reducing function) is a function that accepts an accumulation and a value and returns a new accumulation. They are used to reduce a collection of values down to a single value. | ||
|
||
Reducer's concepts from FP: | ||
|
||
```javascript | ||
[{x:1},{y:2},{z:3}].reduce(function(prev, next){ | ||
return Object.assign(prev, next); | ||
}) | ||
//return {x:1, y:2, z:3} | ||
``` | ||
|
||
In dva, reducers accumulate current model's state. There are some things need to be notice that reducer must be [pure function](https://github.com/MostlyAdequate/mostly-adequate-guide/blob/master/ch3.md) and every calculated data must be [immutable data](https://github.com/MostlyAdequate/mostly-adequate-guide/blob/master/ch3.md#reasonable). | ||
|
||
### Effect | ||
|
||
In dva, we use [redux-sagas](http://yelouafi.github.io/redux-saga/) to control asynchronous flow. | ||
You can learn more in [Mostly adequate guide to FP](https://github.com/MostlyAdequate/mostly-adequate-guide). | ||
|
||
In our applications, the most well-known side effect is asynchronous operation, it comes from the conception of functional programing, it is called side effect because it makes our function impure, and the same input may not result in the same output. | ||
|
||
### Subscription | ||
|
||
Subscriptions is a way to get data from source, it is come from elm. | ||
|
||
Data source can be: the current time, the websocket connection of server, keyboard input, geolocation change, history router change, etc.. | ||
|
||
```javascript | ||
import key from 'keymaster'; | ||
... | ||
app.model({ | ||
namespace: 'count', | ||
subscriptions: { | ||
keyEvent(dispatch) { | ||
key('⌘+up, ctrl+up', () => { dispatch({type:'add'}) }); | ||
}, | ||
} | ||
}); | ||
``` | ||
|
||
## Router | ||
|
||
Hereby router usually means frontend router. Because our current app is single page app, frontend codes are required to control the router logics. Through History API provided by the browser, we can monitor the change of the browser's url, so as to control the router. | ||
|
||
dva provide `router` function to control router, based on [react-router](https://github.com/reactjs/react-router)。 | ||
|
||
```javascript | ||
import { Router, Route } from 'dva/router'; | ||
app.router(({history}) => | ||
<Router history={history}> | ||
<Route path="/" component={HomePage} /> | ||
</Router> | ||
); | ||
``` | ||
|
||
## Route Components | ||
|
||
In dva, we restrict container components to route components, because we use page dimension to design container components. | ||
|
||
therefore, almost all connected model components are route components, route components in `/routes/` directory, presentational Components in `/components/` directory. | ||
|
||
## References | ||
- [redux docs](http://redux.js.org/docs/Glossary.html) | ||
- [Mostly adequate guide to FP](https://github.com/MostlyAdequate/mostly-adequate-guide) | ||
- [choo docs](https://github.com/yoshuawuyts/choo) | ||
- [elm](http://elm-lang.org/blog/farewell-to-frp) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
# Concepts | ||
|
||
[View this in English](./Concepts.md) | ||
|
||
## 数据流向 | ||
|
||
数据的改变发生通常是通过用户交互行为或者浏览器行为(如路由跳转等)触发的,当此类行为会改变数据的时候可以通过 `dispatch` 发起一个 action,如果是同步行为会直接通过 `Reducers` 改变 `State` ,如果是异步行为(副作用)会先触发 `Effects` 然后流向 `Reducers` 最终改变 `State`,所以在 dva 中,数据流向非常清晰简明,并且思路基本跟开源社区保持一致(也是来自于开源社区)。 | ||
|
||
<img src="https://zos.alipayobjects.com/rmsportal/PPrerEAKbIoDZYr.png" width="807" /> | ||
|
||
## Models | ||
|
||
### State | ||
|
||
`type State = any` | ||
|
||
State 表示 Model 的状态数据,通常表现为一个 javascript 对象(当然它可以是任何值);操作的时候每次都要当作不可变数据(immutable data)来对待,保证每次都是全新对象,没有引用关系,这样才能保证 State 的独立性,便于测试和追踪变化。 | ||
|
||
在 dva 中你可以通过 dva 的实例属性 `_store` 看到顶部的 state 数据,但是通常你很少会用到: | ||
|
||
```javascript | ||
const app = dva(); | ||
console.log(app._store); // 顶部的 state 数据 | ||
``` | ||
|
||
### Action | ||
|
||
`type AsyncAction = any` | ||
|
||
Action 是一个普通 javascript 对象,它是改变 State 的唯一途径。无论是从 UI 事件、网络回调,还是 WebSocket 等数据源所获得的数据,最终都会通过 dispatch 函数调用一个 action,从而改变对应的数据。action 必须带有 `type` 属性指明具体的行为,其它字段可以自定义,如果要发起一个 action 需要使用 `dispatch` 函数;需要注意的是 `dispatch` 是在组件 connect Models以后,通过 props 传入的。 | ||
``` | ||
dispatch({ | ||
type: 'add', | ||
}); | ||
``` | ||
|
||
### dispatch 函数 | ||
|
||
`type dispatch = (a: Action) => Action` | ||
|
||
dispatching function 是一个用于触发 action 的函数,action 是改变 State 的唯一途径,但是它只描述了一个行为,而 dipatch 可以看作是触发这个行为的方式,而 Reducer 则是描述如何改变数据的。 | ||
|
||
在 dva 中,connect Model 的组件通过 props 可以访问到 dispatch,可以调用 Model 中的 Reducer 或者 Effects,常见的形式如: | ||
|
||
```javascript | ||
dispatch({ | ||
type: 'user/add', // 如果在 model 外调用,需要添加 namespace | ||
payload: {}, // 需要传递的信息 | ||
}); | ||
``` | ||
|
||
### Reducer | ||
|
||
`type Reducer<S, A> = (state: S, action: A) => S` | ||
|
||
Reducer(也称为 reducing function)函数接受两个参数:之前已经累积运算的结果和当前要被累积的值,返回的是一个新的累积结果。该函数把一个集合归并成一个单值。 | ||
|
||
Reducer 的概念来自于是函数式编程,很多语言中都有 reduce API。如在 javascript 中: | ||
|
||
```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 必须是[纯函数](https://github.com/MostlyAdequate/mostly-adequate-guide/blob/master/ch3.md),所以同样的输入必然得到同样的输出,它们不应该产生任何副作用。并且,每一次的计算都应该使用[immutable data](https://github.com/MostlyAdequate/mostly-adequate-guide/blob/master/ch3.md#reasonable),这种特性简单理解就是每次操作都是返回一个全新的数据(独立,纯净),所以热重载和时间旅行这些功能才能够使用。 | ||
|
||
### Effect | ||
|
||
Effect 被称为副作用,在我们的应用中,最常见的就是异步操作。它来自于函数编程的概念,之所以叫副作用是因为它使得我们的函数变得不纯,同样的输入不一定获得同样的输出。 | ||
|
||
dva 为了控制副作用的操作,底层引入了[redux-sagas](http://yelouafi.github.io/redux-saga/)做异步流程控制,由于采用了[generator的相关概念](http://www.ruanyifeng.com/blog/2015/04/generator.html),所以将异步转成同步写法,从而将effects转为纯函数。至于为什么我们这么纠结于 __纯函数__,如果你想了解更多可以阅读[Mostly adequate guide to FP](https://github.com/MostlyAdequate/mostly-adequate-guide),或者它的中文译本[JS函数式编程指南](https://www.gitbook.com/book/llh911001/mostly-adequate-guide-chinese/details)。 | ||
|
||
### Subscription | ||
|
||
Subscriptions 是一种从 __源__ 获取数据的方法,它来自于 elm。 | ||
|
||
Subscription 语义是订阅,用于订阅一个数据源,然后根据条件 dispatch 需要的 action。数据源可以是当前的时间、服务器的 websocket 连接、keyboard 输入、geolocation 变化、history 路由变化等等。 | ||
|
||
```javascript | ||
import key from 'keymaster'; | ||
... | ||
app.model({ | ||
namespace: 'count', | ||
subscriptions: { | ||
keyEvent(dispatch) { | ||
key('⌘+up, ctrl+up', () => { dispatch({type:'add'}) }); | ||
}, | ||
} | ||
}); | ||
``` | ||
|
||
## Router | ||
|
||
这里的路由通常指的是前端路由,由于我们的应用现在通常是单页应用,所以需要前端代码来控制路由逻辑,通过浏览器提供的 [History API](http://mdn.beonex.com/en/DOM/window.history.html) 可以监听浏览器url的变化,从而控制路由相关操作。 | ||
|
||
dva 实例提供了 router 方法来控制路由,使用的是[react-router](https://github.com/reactjs/react-router)。 | ||
|
||
```javascript | ||
import { Router, Route } from 'dva/router'; | ||
app.router(({history}) => | ||
<Router history={history}> | ||
<Route path="/" component={HomePage} /> | ||
</Router> | ||
); | ||
``` | ||
|
||
## Route Components | ||
|
||
在[组件设计方法](../tutorial/04-组件设计方法.md)中,我们提到过 Container Components,在 dva 中我们通常将其约束为 Route Components,因为在 dva 中我们通常以页面维度来设计 Container Components。 | ||
|
||
所以在 dva 中,通常需要 connect Model的组件都是 Route Components,组织在`/routes/`目录下,而`/components/`目录下则是纯组件(Presentational Components)。 | ||
|
||
## 参考引申 | ||
|
||
- [redux docs](http://redux.js.org/docs/Glossary.html) | ||
- [redux docs 中文](http://cn.redux.js.org/index.html) | ||
- [Mostly adequate guide to FP](https://github.com/MostlyAdequate/mostly-adequate-guide) | ||
- [JS函数式编程指南](https://www.gitbook.com/book/llh911001/mostly-adequate-guide-chinese/details) | ||
- [choo docs](https://github.com/yoshuawuyts/choo) | ||
- [elm](http://elm-lang.org/blog/farewell-to-frp) |