redux
原文发布在我的个人博客 解读只有99行的Redux(一) | 以太空间
一、概述
随着React这个极具革命式函数式思想的前端框架的诞生,Flux模式的前端状态管理框架也随之出现,其中比较著名的就是Flux、Redux和Mbox。Flux是Facebook开发的一种设计模式,旨在保持数据单向流动,当然Flux也存在一些小问题,所以Redux和其他的类Flux库应运而生,它们在实现了Flux思想的同时又具备了自己的特点。
Redux由Dan Abramov和Andrew Clark一起开发,因为Redux的缘故,它们都被邀请加入了Facebook的React团队。在Dan Abramov的github gist页面上有一个slim-redux(代码附在本文末尾),去除了一些复杂的类型判断和错误处理代码,但完整地实现了Redux的所有功能,我们下面就来对这个只有99行的Redux进行解读。
二、createStore解读
我们使用Redux时候最频繁使用的就是这个createStore函数了,一般我们都会这样使用
import { createStore } from 'redux';
const store = createStore(reducer);
reducer这个参数是用户事先定义的数据状态处理函数,一般会以类似下面的方式声明:
// reducer接受state和action并返回新的state
function reducer (state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return [
...state,
{
id: action.id,
text: action.text,
completed: false
}
];
default:
return state;
}
};
reducer的主体是一个switch结构的运算,它只是根据传入的状态数据state和action来判断返回一个新的state。reducer必须是一个纯函数,纯函数主要的含义就是它不可以修改影响输入值,并且没有副作用,副作用指的是例如函数中一些异步调用或者会影响函数作用域之外的变量一类的操作。
另外,注意我们返回新的state时使用的展开操作符的方法,在上面的示例中,这样返回的是一个全新的数组,而不是修改了传入的数组。这也就是我们使用 React 技术栈时尤其需要注意的一点:保证数据的immutability不可变性。
相信大家也知道,createStore这个函数还有第二个的参数,是store的初始状态,也就是说craeteStore函数原型是这样的
function createStore(reducer, initialState) {...}
然后再来看createStore内部这段代码:
var currentReducer = reducer;
var currentState = initialState;
var listeners = [];
var isDispatching = false;
function getState() {
return currentState;
}
前两行就是将传入的reducer参数和state参数进行保存,第三行声明了保存监听函数的数组,第四行的isDispatching是个标识型变量,具体用途下面会说到。
我们知道,当我们调用createStore函数之后,其返回结果(一般写作store)有个获取当前store内部数据状态的成员函数getState,该成员函数实现方法就是直接return内部的currentState,so easy~
接着看subscribe函数的实现:
function subscribe(listener) {
listeners.push(listener);
return function unsubscribe() {
var index = listeners.indexOf(listener);
listeners.splice(index, 1);
};
}
当开发者对store调用subscribe函数的时候,其内部会把传入的监听函数listener参数push到专门存放监听函数的数组listeners里,然后返回一个能取消传入的监听函数的函数unsubscribe作为返回结果,这个unsubscribe实现原理就是将listener记录下来,然后在调用的时候将其从listeners数组里面剔除。
接下来是最重量级选手dispatch函数的实现代码:
function dispatch(action) {
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.');
}
try {
isDispatching = true;
currentState = currentReducer(currentState, action);
} finally {
isDispatching = false;
}
listeners.slice().forEach(listener => listener());
return action;
}
首先来解释一下前面提到的isDispatching,当开发者对store派发(dispatch)一个action时,dispatch内部会调用reducer(currentReducer)并利用传入的action对store内部的状态数据(currentState)进行处理,但是这个过程可能是比较耗时的,所以为了避免对正在进行reducer处理的store再次进行reducer处理,专门用isDispatching来标识当前store内部是否正在进行reducer处理,如果isDispatching为true的话,就不会再对store进行reducer处理,起到一个加锁的作用。
所以disptach内部会首先检测isDispatching是否正处于一个锁死的状态,如果isDispatching为true,说明锁死,正在进行reducer处理,就直接抛出错误。然后在try块内部,首先将isDispatching置为true,对其加锁,再对store内部的状态数据进行处理,处理完后再把isDispatching置为false,将锁打开。
最后遍历listeners,调用所有的监听函数,并返回action。
下面这部分是createStore函数的剩余部分:
function replaceReducer(nextReducer) {
currentReducer = nextReducer;
dispatch({ type: '@@redux/INIT' });
}
dispatch({ type: '@@redux/INIT' });
return { dispatch, subscribe, getState, replaceReducer };
在调用createStore的时候,其内部就派发(dispatch)一个初始action({ type: '@@redux/INIT' }),进行一些初始化的操作。此外,store还有一个用的比较少的成员函数replaceReducer,作用是替换reducer函数,原理很简单,就是将currentReducer指向新的reducer函数,并再次派发一个初始action。
上面这部分代码的最后一行罗列出了store所有成员函数。
以下是解读只有99行的Redux系列的其他两篇文章







网友评论