了解Redux源码
前言
了解redux,最好可以结合一些其他的东西来里了解redux相关原理。
如:
- flux的一些思想
- redux的源码.
- 了解redux-thunk或者其他中间件
- 了解react-redux,或者知道如何使用redux
flux与redux
flux是facebook提出的一种架构模式,而redux是实现了flux思想的一个库,但有点不一样。
flux
- actions:发送数据到dispatcher
- dispacher:协调action与store,即将相应的动作发给相应的store
- store:数据中心,可以存在多个store,所以需要dispatcher进行调度协调
-
view :视图
redux
- actions:将数据修改的动作告知reducer
- reducer:纯函数,在reducer中进行数据的更新
- store:数据中心,是唯一的数据中心
- view:视图
且redux应用的三大原则 - 单一数据源
- state只能是可读的
-
reducer是纯函数
flux与redux
redux库实现了flux的单向数据流,但redux是没有dispatcher的,因为redux只有唯一的一个store,不需要进行调度协调。
在flux架构中,数据逻辑的处理是在各个store中进行的,各自处理各自的,通过调度来进行管理。
而在redux中,数据逻辑的处理和更新是在reducer中进行,并且reducer是一个纯函数,不存在副作用,也更适合数据可追溯。
redux源码
redux源码版本4.0.1
下面是使用redux的代码
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import { createLogger } from 'redux-logger'
import reducer from './reducer'
const loggerMiddleware = createLogger();
const store = createStore (reducer, applyMiddleware(thunkMiddleware,loggerMiddleware));
从使用入口createStore开始
export default function createStore(reducer, preloadedState, enhancer) {
//enhancer 即applyMiddleware()返回的函数, 是一个高级函数
//过滤传参错误 传参不合理的情况
if (
(typeof preloadedState === 'function' && typeof enhancer === 'function') ||
(typeof enhancer === 'function' && typeof arguments[3] === 'function')
) {
throw new Error(
'It looks like you are passing several store enhancers to ' +
'createStore(). This is not supported. Instead, compose them ' +
'together to a single function.'
)
}
//preloadedState可不传,若是只传入两个参数,第二个为函数,则enhancer = preloadedState
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
//如果使用中间件,那么enhancer会进入这个判断,
return enhancer(createStore)(reducer, preloadedState)
}
// 其他代码...
// 主要是dispatch 和 subscribe ,一般是其他库进行调用来实现发布订阅,如react-redux
// 进行dispatch,对state进行初始化
dispatch({ type: ActionTypes.INIT })
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
}
applyMiddleware
export default function applyMiddleware(...middlewares) {
// 这里就是enhancer
// enhancer(createStore)(reducer, preloadedState) 其实就是返回的 {..store,dispatch}
// 这里是用箭头函数写的,如果乍一看有点没太理解,可以捋一捋
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = () => {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.'
)
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
//将中间件遍历一遍, chain 数组中 值 类似于函数 next=>action=>{}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
//compose 进行函数组合
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
中间件redux-thunk
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => (next) => (action) => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
梳理下
//上面就是redux-thunk的源码
//实际就是中间件就是这一部分
({ dispatch, getState }) => (next) => (action) => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
//将中间件遍历一遍时,都执行了一次,并返回,所以chain数组的里面的值就是类似于函数 next=>action=>{} 结构
const chain = middlewares.map(middleware => middleware(middlewareAPI))
//重点,中间件串联使用的关键
dispatch = compose(...chain)(store.dispatch)
先来看下compose函数
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
梳理下
如果 composeFn = compose(fn1,fn2,fn3,fn4);
那么composeFn=fn1(fn2(fn3(fn4(x))));
下面是模拟compose和中间件 的例子
const compose = (...funcs) => {
return funcs.reduce((a, b) => (...args) => a(b(...args)));
};
function fn1(next) {
console.log("1");
return (action) => {
console.log("11");
return next(action);
};
}
function fn2(next) {
console.log("2");
return (action) => {
console.log("22");
return next(action);
};
}
function fn3(next) {
console.log("3");
return (action) => {
console.log("33");
return next(action);
};
}
function fn4(next) {
console.log("4");
return (action) => {
console.log("44");
return next(action);
};
}
let x = () => () => {
console.log("xx");
};
let a = fn1(fn2(fn3(fn4(x))));
let composeFn = compose(
fn1,
fn2,
fn3,
fn4
);
let b = composeFn(x);
a();
b();
//结果是
//4321 4321 11223344 11223344
compose(...chain)函数将中间件从右向左进行了包裹,即上面的4321
dispatch = compose(...chain)(store.dispatch) 执行得的了新的dispatch,即扩展后的dispatch,即上面的b。
当用户发送action,action调用了扩展后的dispatch时,会发生从左到右的执行顺序,即11223344。这是因为中间件基本是这样的高级函数结构:next=>action=>{},当compose(...chain)(store.dispatch)执行时,最内侧的函数先执行将action=>{},作为参数传给了外层函数,即右则结果函数作为参数传递了左侧的函数,以此类推,所以当扩展dispatch执行时是从左向右的顺序。
参考
1.The difference between Flux and Redux——Samer Buna
2.MVC vs Flux vs Redux – The Real Differences——Vinugayathri
3.深入浅出Redux原理——呼吸
4.redux之compose——limengke123
5.带着问题看React-Redux源码——Nero