最近比较空闲,就想看点源码,学习记录一下。如果自己收获能给他人些帮助,是一件开心的事也会更有动力。今天的主角是Redux。
先唠叨几句耳熟能详的话
Redux 是 JavaScript 状态容器,提供可预测化的状态管理。
Redux三大原则:
- 单一数据源
- State 只能通过触发Action
- 使用纯函数来执行修改
Redux的源码很少,5个核心文件。
QQ截图20201010152216.png1. 首先来看createStore.js,一切的开始。
const store = createStore(reducer, preloadedState, enhancer);
这个函数默认接收三个参数(改变状态的方法,状态的初始值,增强器(增强后的store)),返回的store对象中包含dispatch,getState,subscribe等方法。(笔者只列出在工作中常用的)。
这个时候,代码应该是这样的
function createStore(reducer, preloadedState, enhancer) {
function dispatch() {}
function getState() {}
function subscribe() {}
return {
dispatch,
getState,
subscribe
};
}
接下来,就要对传入参数做判断,按规矩办事。这里就直接贴源码了,都是条件判断。
image.png
重点注意下,红框中的部分。至于这里为什么这么写,后面细说。不耽误理解createStore这个函数。
下面将这个三个函数分别简单实现
- getState 从简单入手,将初始的state或者改变后的state返回。代码变成这样
function createStore(reducer, preloadedState, enhancer) {
let currentState = preloadedState;
function getState() {
return currentState;
}
return { getState };
}
- subscribe 使用发布订阅模式,收集每一个订阅state的函数。
function createStore(reducer, preloadedState, enhancer) {
let currentListeners = []
let nextListeners = currentListeners
function subscribe(listener) {
// ensureCanMutateNextListeners() 这个函数在官方的解释为,进行浅拷贝,防止分发过程中,增加/取消订阅事件,而引发出问题。先去掉
nextListeners.push(listener)
return function unsubscribe() {
// nextListeners.splice(index, 1)
}
}
return { subscribe };
- dispatch 在dispatch的时候,会改变state,reducer返回新的state,所以dispatch内部调用reducer。订阅者能收到消息,说明subscribe订阅的函数会执行。
function createStore(reducer, preloadedState, enhancer) {
let currentState = preloadedState;
let currentReducer = reducer;
let nextListeners = currentListeners
function dispatch(action) {
currentState = currentReducer(action);
for (let i = 0; i < nextListeners .length; i++) {
const listener = listeners[i]
listener()
}
return action;
}
// 默认会执行一次 dispatch,简单写
// dispatch();
return {
getState,
dispatch,
subscribe
};
}
不传入增强器enhancer时,代码大致是这样
function createStore(reducer, preloadedState, enhancer) {
let currentState = preloadedState;
let currentReducer = reducer;
let currentListeners = []
let nextListeners = currentListeners
function getState() {
return currentState;
}
function dispatch(action) {
currentState = currentReducer(action);
for (let i = 0; i < nextListeners .length; i++) {
const listener = listeners[i]
listener();
}
return action;
}
function subscribe(listener) {
// ensureCanMutateNextListeners();
nextListeners.push(listener)
return function unsubscribe() {}
}
return { getState, dispatch, subscribe };
}
现在,我们来看传入enhancer的情况。这里要用到applyMiddleware.js中的applyMiddleware函数,这个函数返回增强后的store,将dispatch方法增强。让原来一步操作的dispatch(action),变成执行func1->func2->...->store.dispatch(action)。
// 上面createStore.js -> enhancer(createStore)(reducer, preloadedState) => { getState, dispatch, subscribe } 这个是调用applyMiddleware()函数后的返回函数enhancer,enhancer执行2次最后返回对象store,由此推测代码应该是个样子的
function applyMiddleware(...middlewares) {
return () => () => ({ getState, dispatch, subscribe })
}
第一步要有store,才能增强其dispatch方法,获取store的唯一途径是调用createStore函数,将reducer和initialState传入。现在改成这样
// applyMiddleware(...middlewares) -> enhancer(createStore)(reducer, preloadedState)
// enhancer(createStore) -> (...args) => { const store = createStore(...args); return store; }
// enhancer(createStore)(reducer, preloadedState) => store;
function applyMiddleware(...middlewares) {
return (createStore) => (...args) => {
const store = createStore(...args);
return store;
}
}
下面,把中间件引进来,我们知道中间件是层层包裹的,经过中间件后,返回的dispatch再调用dispatch时,会把外层函数都执行了,最后再执行dispatch(action)。可能是这样
function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args);
let dispatch = compose(...middlewares)(store.dispatch);
return { ...store, dispatch };
}
}
compose.js ,返回一个函数
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)));
}
个人猜测这里中间件如果这样写compose(...middlewares)(store.dispatch),中间件的只能操作dispatch,将这个store整体传入又不安全。如果给每个中间件都注入一个getState,dispatch。就能做更多的事情。代码可能变成这样
function applyMiddleware(...middlewares) {
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,
// 在redux 3.x的版本源码,都是这样的 dispatch: (...args) => (store.dispatch)(...args); 这好理解每个中间件通过作用域链拿到dispatch,接下来就按照3.x的说。
// 4.x版本的写法,自己没想明白,上面的英文解释说,防止在构建时调用dispatch,我自己还没有领会到精髓,了解的朋友请帮忙指点迷津
dispatch: (...args) => dispatch(...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
let dispatch = compose(...chain)(store.dispatch);
return { ...store, dispatch };
}
}
这样,就可以在每次调用dispatch时,由中间件控制dispatch何时调用。
最后,再来看一下中间件的写法。({getState, dispatch}) => next => action => {},格式固定。
以redux-thunk为例,简单说一下。
// 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;
最简单的使用方式,当参数是函数时,就执行这个函数并将dispatch、getState作为参数传入。
store.dispatch(
dispatch => {
setTimeout( () => {
dispatch(action)
},1000)
}
);
第一次写博客,有点慌张语言的组织不太好,有些地方还有点啰嗦。如果个人理解偏差,请大家不吝赐教,共同进步。
网友评论