您的位置:首页 > Web前端 > React

React Native + Flux

2016-07-29 15:37 281 查看

1、基于React的Flux架构

React 是基于View层的前端库,其核心是使用组件定义界面。React是组件化和状态机的思想,对于组件与组件、组件与数据模型之间的通信,页面的渲染,需要一套机制来组织整个应用的状态。Flux即是这样一种用来构建React的应用程序加架构,最大的特点是单向数据流,分层解耦。比较适用于大型项目。基本框架如下:



flux包括了四个东西:

view: 视图 controller调用action行为函数,实现change回调

action:动作实现action在dispatcher中的注册

dispatcher:派发器调用store中数据更新以及change 提交功能函数

store:数据层实现数据更新以及事件接口回调

应用共用一个派发器,一般View的数据变化主要有两个方面:来自服务器端的数据和用户交互数据。对于用户交互的数据,单向数据流的主要流程是:用户交互数据在View Controller中将数据交互封装成Action,并把该Action通过dispatch()函数携带actionType加入到在派发器中;派发器通过register()函数,将不同类型的action分发到不同的数据层进行处理(调用数据层中的不同函数),并提交事件(emit);数据层Store需要编写不同的数据处理函数,来进行本地数据存储的更新(一般这个函数的编写与Action对应),Store需要在变动后向View发送"change"事件,因此必须实现事件接口(继承EventCommitter.prototype,调用Store.emit()和store.on()实现)。

注:1、view controller 保存状态,转发给子组件,这样保证子组件不含任何状态,从而进行组件复用。

2、事件接口的实现在Store中,Dispatcher中更新Store,提交change event;Controller view中监听change event,从Store中拉取数据, 传给子组件。

3、基于React的Demo:https://github.com/xianyulaodi/myFluxTest

2、Flux在RN中的应用

Flux当初是针对React提出的,RN是基于React的衍生品,以下针对RN进行Flux的应用研究。

Demo流程图:



demo地址:https://github.com/saralalala/flux-RN

3、Flux主流框架Redux(使用React-Redux库)

Redux是Flux的一种框架实现,Redux 放弃了 event emitters(事件发送器),转而使用纯 reducer。使用更方便简洁:




(1)发起action:实现action在dispatcher中的注册(把action创建函数传到到dispatch()方法上或者创建被绑定的action函数

1)action创建函数传到到dispatch()方法上:
function addTodo(text) { return { type: ADD_TODO, text }}
dispatch(addTodo(text))
2)创建一个被绑定的action创建函数:
const boundAddTodo = (text) => dispatch(addTodo(text));
boundAddTodo(text);[/code][/code]


(2)reducer:纯函数,接收旧的state和action,返回新的state(注意不要修改旧的state值)


(previousState, action) => newState


reducee根据业务逻辑可以有很多个,最终传给Store的只有一个,可以通过
combineReduces()
合成reducer,生成一个函数。这个函数根据reducer 的
key 来筛选出 state 中的一部分数据并处理,然后这个生成的函数再将所有 reducer 的结果合并成一个大的对象,用来创建。


(3)store: 确定state对象结构


1)一个应用中只有唯一一个Store;


2)Store以树的形式保存了整个应用的State,本质上是一个对象。主要职责:


 



维持应用的 state;
提供
getState()
方法获取
state;
提供
dispatch(action)
方法更新
state(发布);

通过
subscribe(listener)
注册监听器,订阅Store树的监听;
replaceReducer()
动态替换reducer函数

3)建议格式:todosById: { id -> todo }
todos: array<id>


redux数据流程源码分析:

当state数据变化时,会触发所有订阅了该事件的监听器,在dispatch中循环执行监听器回调函数,从而在回调函数中进行相应处理.

1、事件的订阅通过subscribe(listener)执行

2、事件的触发在dispatch(action)中

源码:
(1)store.subscribe()

function subscribe(listener) {

if (typeof listener !== 'function') {

throw new Error('Expected listener to be a function.')

}

var isSubscribed = true;

ensureCanMutateNextListeners()

nextListeners.push(listener);

//返回接口,正好利用闭包可以保持对相应Listener的访问

return function unsubscribe() {

if (!isSubscribed) {

return

}

isSubscribed = false

ensureCanMutateNextListeners()

var index = nextListeners.indexOf(listener)

nextListeners.splice(index, 1)

}

}

(2)store.dispatch(action)

/**

* 唯一改变state的接口

* 生成nextState同时通知观察者

* 每次dispatch都会执行state的观察者

*

* return action这样设计比较好的一点是方便扩展中间件!!!

*/

function dispatch(action) {

/**

* 错误处理,可以跳过

*/

if (!isPlainObject(action)) {

throw new Error(

'Actions must be plain objects. ' +

'Use custom middleware for async actions.'

)

}

if (typeof action.type === 'undefined') {

throw new Error(

'Actions may not have an undefined "type" property. ' +

'Have you misspelled a constant?'

)

}

if (isDispatching) {

throw new Error('Reducers may not dispatch actions.')

}

/**

* 将当前状态和action作为参数传值给reducer,生成下个状态

*/

try {

isDispatching = true

currentState = currentReducer(currentState, action)

} finally {

isDispatching = false

}

var listeners = currentListeners = nextListeners

for (var i = 0; i < listeners.length; i++) {

/**

* 遍历subscribe的观察者

*/

listeners[i]()

}

return action

}

(4)在React中的应用:需要React-Redux
提供API(Provider/Connect)
Provider和Connect是React-Flux提供的接口,负责把React Component 和 Redux store 结合起来:

Provider 把可以将从
createStore
返回的store放入context中,使子集可以获取到store并进行操作;

<Provider
store={store}>{() => <App />}</Provider>

connect的两个可选参数,前两个

[mapStateToProps(state, [ownProps]): stateProps]
:
第一个可选参数是一个函数,只有指定了这个参数,这个关联(connected)

组件才会监听 Redux Store 的更新,每次更新都会调用
mapStateToProps
这个函数,返回一个字面量对象将会合并到组件的

props
属性。
ownProps
是可选的第二个参数,它是传递给组件的
props
,当组件获取到新的
props
时,
ownProps


都会拿到这个值并且执行
mapStateToProps
这个函数。

[mapDispatchProps(dispatch, [ownProps]):
dispatchProps]
: 这个函数用来指定如何传递
dispatch
给组件,在这个函数里面

直接 dispatch action creator,返回一个字面量对象将会合并到组件的
props
属性,这样关联组件可以直接通过
props
调用

action
, Redux 提供了一个
bindActionCreators()
辅助函数来简化这种写法。
如果省略这个参数,默认直接把

dispatch
作为
props
传入。
ownProps
作用同上。

组件的更新:

redux 中,没有与 redux 有直接关联的组件称为木偶组件,不理外面纷纷扰扰,只知道自己拥有了 state 及 具备操作 state 数据的 actions 方法。

当木偶组件使用 actions 方法,更新了 store.state 的数据时,将会触发 store 中的 subscribe 所注册的函数。而其中一个注册函数,就在 Connect 组件中静默注册了。

我们把connect返回的函数叫做Connector,它返回的是内部的一个叫Connect的组件,它在包装原有组件的基础上,还在内部监听了Redux的store的变化,

为了让被它包装的组件可以响应store的变化:

trySubscribe()
{ if
(shouldSubscribe && !this.unsubscribe) {
this.unsubscribe =
this.store.subscribe(::this.handleChange)
this.handleChange() }}

handleChange ()
{ this.setState({
storeState: this.store.getState()
})}

但是通常,我们connect的是某个Container组件,它并不承载所有App state,然而我们的handler是响应所有state变化的,于是我们需要优化的是:当storeState变化的时候,仅在我们真正依赖那部分state变化时,才重新render相应的React组件,那么什么是我们真正依赖的部分?就是通过
mapStateToProps
mapDispatchToProps
得到的。

具体优化的方式就是在
shouldComponentUpdate
中做检查,如果只有在组件自身的props改变,或者
mapStateToProps
的结果改变,或者是
mapDispatchToProps
的结果改变时
shouldComponentUpdate
才会返回true,检查的方式是进行shallowEqual的比较。
// 在 Connect 中

this . store . subscribe ( this . handleChange . bind ( this ) ) ;

即当 actions 更改了 state 时,会调用注册函数 handleChange。从而进行 “阿米诺骨牌式” 的函数执行连锁反应。更新了 state,并使用新的数据重新 render 组件。
实际上是为智能组件(传入
connect 的组件)传入新的 props,因为各个子元素是通过引用父级组件的 props,所以将进行一级一级的差异数据更新(PureRenderMixin

),最终效果就是页面更新了。

实际上,这里与简单的发布订阅模式类似。使用
store.subscribe(cb); 来订阅一个回调函数,子组件进行 action 操作 store.state 时进行发布,执行了回调函数。

通过connect 获得store中的state,并转化成Component的props来使用。(以下为引用Wang Namelos在知乎的回答)

1. React有props和state: props意味着父级分发下来的属性,state意味着组件内部可以自行管理的状态,并且整个React没有数据向上回溯的能力,也就是说数据只能单向向下分发,或者自行内部消化。

理解这个是理解React和Redux的前提。

2. 一般构建的React组件内部可能是一个完整的应用,它自己工作良好,你可以通过属性作为API控制它。但是更多的时候发现React根本无法让两个组件互相交流,使用对方的数据。

然后这时候不通过DOM沟通(也就是React体制内)解决的唯一办法就是提升state,将state放到共有的父组件中来管理,再作为props分发回子组件。

3. 子组件改变父组件state的办法只能是通过onClick触发父组件声明好的回调,也就是父组件提前声明好函数或方法作为契约描述自己的state将如何变化,再将它同样作为属性交给子组件使用。

这样就出现了一个模式:数据总是单向从顶层向下分发的,但是只有子组件回调在概念上可以回到state顶层影响数据。这样state一定程度上是响应式的。

4. 为了面临所有可能的扩展问题,最容易想到的办法就是把所有state集中放到所有组件顶层,然后分发给所有组件。

5. 为了有更好的state管理,就需要一个库来作为更专业的顶层state分发给所有React应用,这就是Redux。让我们回来看看重现上面结构的需求:

a. 需要回调通知state (等同于回调参数) -> action

b. 需要根据回调处理 (等同于父级方法) -> reducer

c. 需要state (等同于总状态) -> store

对Redux来说只有这三个要素:

a. action是纯声明式的数据结构,只提供事件的所有要素,不提供逻辑。

b. reducer是一个匹配函数,action的发送是全局的:所有的reducer都可以捕捉到并匹配与自己相关与否,相关就拿走action中的要素进行逻辑处理,修改store中的状态,不相关就不对state做处理原样返回。

c. store负责存储状态并可以被react api回调,发布action.

当然一般不会直接把两个库拿来用,还有一个binding叫react-redux, 提供一个Provider和connect。很多人其实看懂了redux卡在这里。

a. Provider是一个普通组件,可以作为顶层app的分发点,它只需要store属性就可以了。它会将state分发给所有被connect的组件,不管它在哪里,被嵌套多少层。

b. connect是真正的重点,它是一个科里化函数,意思是先接受两个参数(数据绑定mapStateToProps和事件绑定mapDispatchToProps),再接受一个参数(将要绑定的组件本身):

mapStateToProps:构建好Redux系统的时候,它会被自动初始化,但是你的React组件并不知道它的存在,因此你需要分拣出你需要的Redux状态,所以你需要绑定一个函数,它的参数是state,简单返回你关心的几个值。

mapDispatchToProps:声明好的action作为回调,也可以被注入到组件里,就是通过这个函数,它的参数是dispatch,通过redux的辅助方法bindActionCreator绑定所有action以及参数的dispatch,就可以作为属性在组件里面作为函数简单使用了,不需要手动dispatch。这个mapDispatchToProps是可选的,如果不传这个参数redux会简单把dispatch作为属性注入给组件,可以手动当做store.dispatch使用。这也是为什么要科里化的原因。

demo地址:demo地址:https://github.com/saralalala/redux-RN

5、参考文章:

1)http://www.ruanyifeng.com/blog/2015/03/react.html;廖雪峰React

2)http://www.cnblogs.com/xianyulaodi/p/5358315.html;Flux学习笔记

3)http://cn.redux.js.org/docs/introduction/ThreePrinciples.html ; Redux中文官网

4)http://www.jianshu.com/p/09956d82bca6;Redux解析

5)http://www.zhihu.com/question/41312576?sort=created ;



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  react native