端午来一发。
Redux
被大家知道应该都是来自于 React
的出现,但是这篇文章会通过介绍一个 React
之外的例子,来让大家理解 Redux
是什么东西,到底还能够做点撒。
我自己的理解是:
Redux 是一个改变状态(state)的模型,这个模型通过一个单向操作的方式来改变状态,用数学符号的方式来理解我认为应该是:
y = f(x)
,嗯,就是一个最简单的函数模型。
不过这样说,有些人还会不理解,毕竟以前撸代码不是这个样子的,那么我们先反向的来获取自己的理解。
首先,我们需要知道 Redux 有的以下几个东西:
我觉得需要介绍清楚 Redux ,知道这三个就行了,至于还有一些函数我觉得都是调味料,这里先不介绍。
好吧,现在我就把上面的数学函数模型语义的转换一下(也许不一定对,但是可能暂时这样理解,理解是一个过程):
newState = store(reducer)(state)
newState = reducer(state)
为什么这里可能理解成两个步骤呢?那是因为我们可以从不同的层面去认识 Redux,在第一步中我的理解是store里面的东西(reducer)去改变了 state,而在第二步中,我以实际会起到作用的层面去理解,就是 reducer 去改变了 state。
好吧,来个对比:
y = f(x)
newState = reducer(state)
最后我自己先得出一个结论:
f === reducer ( ===: 表示等价的意思, w3ctech 好像 markdown 语法不一样...)
好吧,废话就上面那么多了,接下来我举一个实际的例子来理一下自己对 Redux 的理解,这个例子是我在一篇文章中看到的,觉得不错。
这里我们要实现的效果就是:
点击飞机,让飞机随机移动到一个新的坐标点
这里我们可以理解为:新坐标[x1, y1] = f(原始坐标[x0, y0])
1.首先我们需要定义个 f
来修改坐标
/**
* reducer
* @param state 坐标状态
* @param action 执行的行为
*/
function coordinates(state = [1, 1], action) {
switch (action.type) {
case CLICK:
return [
state[0] + 40 * Math.random(),
state[1] + 40 * Math.random()
]
default:
return state;
}
}
这个函数主要就是使初始坐标 [1, 1] => [1 + random, 1 + random] 这样返回一个全新的数组,这个全新的数组就是新的状态。
2.我们再将这个 f
函数给 Redux 的 Store 去管理
/**
* 创建 Redux 的 Store(存储器)
*/
let store = createStore(coordinates);
这个比较简单,就是通过传入 f
来创建一个 Store,对于 Redux 来说有且只有一个 Store,这个和 flux 有比较大的差别。
3.然后再定义一个行为,使我们能够通过 dispatch 调用这个行为去改变 state
/**
* action
*/
const CLICK = 'CLICK';
/**
* @returns plainObject => action
*/
function click() {
return {
type: CLICK
}
}
在 Redux 中,只有通过 dispatch
方法去改变状态,就是因为这样,才让我们调试代码变得简单,复现 BUG 更加容易,因为它是单向的,顺着一个方向在流动数据。
4.接下来就写一下 dispatch
plane.events.onInputDown.add(() => {
store.dispatch(click())
}, null);
先抛开其他代码,我们剥离一下:
store.dispatch(click())
这样就会去调用行为,然后执行我们上面定义的 coordinates
函数,返回一个新的坐标
5.最后我们再订阅一下具体改变我们飞机位置的业务代码
上面的写完后,我们只是返回来状态,但是还没体现到飞机在场景下的移动效果。我们能够通过
store.getState()
来获取最新的当前状态,其实在 Redux 源代码里面就是很简单的一个函数:
function getState() {
// 这里的 currentState 是一个外部的变量,在 Store 的作用域下全局生效
return currentState;
}
好吧,现在我们让飞机订阅一下移动的函数:
/**
* 定义 plane(飞机) 的移动
*/
function movePlane(plane) {
game.add.tween(plane).to({
x: store.getState()[0],
y: store.getState()[1]
}, 1000, 'Linear', true);
}
/**
* 移动事件的订阅
*/
store.subscribe(movePlane.bind(null, plane));
大家会想,为什么我订阅了就自动调用 movePlane 生效了呢?在 Redux 内部会调用当前的监听器,源代码如下,很简单,监听器就是一个数组来装的,最后移出来执行一下:
function subscribe(listener) {
var isSubscribed = true;
ensureCanMutateNextListeners();
nextListeners.push(listener);
// 然后会返回一个取消订阅的函数,这里省略
}
function dispatch(action) {
// 省略一些杂七杂八的异常处理代码
try {
isDispatching = true;
currentState = currentReducer(currentState, action);
} finally {
isDispatching = false;
}
// 执行的代码
var listeners = currentListeners = nextListeners;
for (var i = 0; i < listeners.length; i++) {
listeners[i]();
}
return action;
}
好吧,这样就是一个游戏的流程来,在 Redux 的帮助下,很好的组织了代码,优雅的完成了。执行代码的流程是这样的:
dispatch(click()) => update reducer => subscribe() => 飞机移动新位置
这个例子很好的解释了脱离 React 的束缚,很纯粹的 Redux,原文翻译有点慢,干脆就自己写了一下。
项目所有的代码:https://github.com/fegg/redux-game
参考英文原文:http://atendesigngroup.com/blog/redux-outside-react
扫码关注w3ctech微信公众号
个人建站,做技术问答的,目前最火的是前端领域,欢迎一起交流!——http://www.dreawer.com
共收到1条回复