标签:基本概念 lang creates onclick 使用场景 多少 否则 结合 document
前几天给大家谈了谈React
不过它只是一个侧重于UI的框架
只能算作是MVC中的V(View视图)
而且只是DOM的一个抽象层,不是Web应用完整解决方案
如果仅仅用它构建大型项目
你会非常的吃力
14年,Facebook提出Flux架构意图解决这个问题
15年,Dan Abramov将 Flux 与函数式编程相结合,创造了Redux,由于简单易学就开始流行起来
16年,Dan Abramov被facebook挖走了
Redux体积很小,如果删掉源码的空行和注释,连500行代码都不到
别看它小(压缩版7KB),却非常有用
使用它构建web应用有如下优点:
可能有同学会说,这不是和百度百科一样么
这应该不能算抄吧,其实百度百科redux的词条是我建的o( ̄▽ ̄)d
不过编辑的蛮差劲的,希望各位有时间的时候能补充词条让更多人了解
还要强调的一点就是,不是任何时候都需要用它
如果我们用React遇到瓶颈了,或许我们才需要用到Redux
一般情况它都是配合React使用的(因为React用state描述界面,Redux控制state,配合天衣无缝)
不过也可以配合其他框架(甚至原生JavaScript)
阮大神在这方面总结的很详细
这些情况不需要用Redux
下列情况建议使用Redux
组件角度考虑的话,Redux的使用场景
学Redux的时候就觉得很亲切,但不知道亲切在哪儿
后来知道了,这不就是命令模式么
而这个命令command对象就是store
命令模式的应用场景就是:有时需要向某些对象发送请求,但是不知道请求的接收者是谁,
也不知道被请求的操作是什么,此时希望用一种松耦合的方式来设计软件,
使得请求发送者和请求接收者能够消除彼此之间的耦合关系
而我们经常会遇到这样的问题
触发事件后,必须向某些负责具体行为的对象发送请求(接受者receiver)
但还不知道接收者是什么,也不到它要做什么,这是我们就需要命令对象command解耦
所以说白了Redux就是一种设计模式——命令模式
(可见设计模式有多重要,有时间真的该深入研究一下)
Redux设计思想就两句话,切记
(再次引用阮大神的总结)
其实Redux架构并不难
文件小,概念也没有多少
最最重要的就是这个
Redux中单一状态树这个概念很重要
什么是单一状态树呢?(也叫单一数据源)
所有的状态state对象(数据)都以树的形式储存唯一的store中
页面中的任何变动,首先都要去改变状态树(store),然后以某种形式呈现到页面
还记得刚刚了解的Redux设计思想吗
一个视图对应一个状态对象
举个例子
比如我们的一个state对象
{
text: ‘a text‘,
color: ‘red‘
}
它可能展示一个这样的视图
<p style="color: red;">a text<p>
现在状态改变了,变成了这个样子
{
text: ‘another text‘,
color: ‘green‘
}
状态改变了,视图也随之改变
<p style="color: green;">another text<p>
Redux核心的概念就三个:Action、Reducer、Store
Action是一个对象,表示页面发生的事情,也就相当于事件
内部其实就是简单的具有一个type属性(常为常量)的JS对象,
描述了Action的类型以及负载信息
比如上面我们视图变化的例子
//Action
{
type: ‘TEXT_CHANGE‘,
newText: ‘another text‘,
newColor: ‘red‘
}
考虑到对它的复用,Action可以通过生成器(Action Creators)来创建
其实它就是返回Action对象的函数(我们自定义的函数)
参数可以适情况而定
//Action Creators
function change(text, color){
return {
type: ‘TEXT_CHANGE‘,
newText: text,
newColor: color
}
}
Reducer是一个函数,用于修改我们的单一状态树
还记得ES5的Array.prototype.reduce()函数么
我认为reduce中文释义中最接近这个函数功能的应该是“浓缩”
这个浓缩函数接收一个回调让我们将多个值浓缩为一个值
我们的reuder就可以作为(包含多个action对象的)数组的回调函数
Reducer是一个纯函数(Pure Function)
(相同的输入会得到同样的输出)
纯函数(Pure Function):
概念源于函数式编程
- 不能改写参数
- 不能调用系统I/O的API
- 不能调用Math.random()或Date.now()等不纯方法
(相同的输入要得到同样的输出)
它获取应用的state和action作为参数,通过判断action的type属性返回一个新的state
伪代码表示就是:state + action => state
我们例子中的Reducer可以这样来写
//Reducer
const initState = {
text: ‘a text‘,
color: ‘red‘
}
function reducer(state = initState, action){
switch(action.type){
case ‘TEXT_CHANGE‘:
return {
text: action.newText,
color: action.newColor
}
default:
return state;
}
}
由于纯函数这个条件的限制
所以我们不能改写参数中的state
所以比如说我们的state要表示列表
于是使用了数组来表示state
那我们就不能使用state.push()
如果state是对象也是同样的道理
可选方案如下:
只要记住一点就好了,在reducer中不能改变参数state
上面我们说到了,整个应用的数据都保存在一个store中,就导致store非常庞大
可想而知,reducer也会十分巨大
不过,Redux库为我们提供了combineReducers( )函数
让我们可以自定义很多reducer函数,然后通过它来合并成一个大reducer
参数就是一个对象
import {combineReducers} from ‘redux‘;
const reducer = combineReducers({a,b,c});
这样利用ES6的语法,会让state的属性名与reducer的函数名相同
如果不同名的话,可以这样写
const reducer = combineReducers({
a: reducerA(state.a, action),
b: reducerB(state.b, action),
c: reducerC(state.c, action)
});
模拟实现一个combineReducers( )函数
const combineReducers = reducersObj => {
return (state = {}, action) => {
return Object.keys(reducersObj).reduce(
(nextState, key) => {
nextState[key] = reducersObj[key](state[key], action);
return nextState;
},{});
};
};
Store是一个对象,它保存应用的状态并提供一些方法来存取状态,分发状态以及注册监听
全部state由一个Store来表示,Store维持着应用的state
Store 把Reducer和Action联系到一起
API如下:
API的使用也非常简单
比如我们试着这样做
store.subscribe(()=>console.log(store.getState()));
store.dispatch(change(‘another text‘,‘green‘));
//输出:{color:‘green‘,text:‘another text‘}
首先我们(subscribe)绑定了一个listener
这个listener会让应用state更新后,就输出当前state(getState)
最后(dispatch)更新状态
我们可以通过redux库中的createStore( )方法来创建Store
参数如下:
return enhancer(createStore)(reducer, preloadedState)
import {createStore} from ‘redux‘;
const store = createStore(reducer);
//或者干脆直接导入API
//let {getState,dispatch,subscribe} from createStore(reducer)
了解了它的功能,我们可以试着模拟一个简单的createStore( )函数
const createStore = (reducer, preloadedState, enhancer) => {
if(enhancer){
return enhancer(createStore)(reducer, preloadedState);
}
let state = preloadedState,
listeners = [];
const getState = () => state;
const dispatch = (action) => {
state.reducer(state, action);
listeners.forEach(listener => listener());
}
const subscribe = (listener) => {
listeners.push(listener);
let index = listeners.length;
return () => listeners.splice(index, 1);
}
return {
getStore,
dispatch,
subscribe
}
}
这个只是帮助大家理解,所以没有写太细
更详细的可以参考源码
如果了解了核心概念后一脸懵逼不要紧
继续往下看就懂了
使用React配合Redux构建项目的工作流如下:
Store由Redux的API-createStore(reducer)创建
页面触发事件由Action Creators返回action转发给Store
Store再将当前state和收到的action转给Reducer
Reducer处理后返回一个新state反馈给Store
Store管理的state改变了
就会引发React组件的重新渲染
是不是发现Redux也就这点儿东西,豁然贯通 ︿( ̄︶ ̄)︿
(其实这只是最最基本的Redux,还有很多有用的API)
当然如果仅仅是理解了,也只是纸上谈兵
由于本文只是浅谈Redux
就讲一个小小demo加深一下对Redux的理解
这个实例就是一个简单的手动计数器
既然由Redux来管理页面的state
React组件中就不必使用state了
我们使用React的时候都是利用props、state改变而触发内部的render重绘
但现在我们利用React+Redux
就要自定义一个render函数再利用store.subscribe()绑定
这样store管理的state改变,就会触发我们自定义的render函数重绘页面
结构以及绑定的事件
事件处理函数也很简单,将action传递给store
const Counter = React.createClass({
reduceHandler(){
store.dispatch({type: ‘REDUCE‘});
},
addHandler(){
store.dispatch({type: ‘ADD‘});
},
render(){
return (
<div>
<p>{this.props.value}</p>
<button onClick={this.reduceHandler}>-</button>
<button onClick={this.addHandler}>+</button>
</div>
)
}
});
构建reducer和store
const reducer = (state = 0, action) => {
switch (action.type) {
case ‘ADD‘: return state + 1;
case ‘REDUCE‘: return state - 1;
default: return state;
}
};
const store = createStore(reducer);
创建渲染listener并绑定,每次store改变,触发重绘
const render = () => {
ReactDom.render(
<Counter value={store.getState()}/>,
document.getElementById(‘root‘)
);
};
store.subscribe(render);
不要忘了首次需要进行手动渲染
否则页面什么都没有
render();
标签:基本概念 lang creates onclick 使用场景 多少 否则 结合 document
原文地址:http://blog.csdn.net/q1056843325/article/details/54784109