Redux 源码共读-1
学习前,我在这里插入图片描述们先来看下源码的目录结构:
就src
下的 6 个文件 和 utils
下的 3 个文件。量不多哈。我们先从 utils
下开始:
一、Utils
1、actionTypes.ts
const randomString = () => Math.random().toString(36).substring(7).split('').join('.');
const ActionTypes = {
INIT: `@@redux/INIT${randomString()}`,
REPLACE: `@@redux/REPLACE${randomString()}`,
PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION${randomString()}`
}
export default ActionTypes;
抛出两个初始的 action
, 其实就是一个命名,增加了7位的随机数。toString(36)
代表随机数的范围:数字+26个字母
2、warning.ts
export default function warning(message: string): void {
if(typeof console !== undefined && typeof console.error === 'function') {
console.error(message);
}
try {
throw new Error(message)
} catch(e) {}
}
给 console
增加一层判断,ie8 一下都不支持 console。
3、isPlainObject.ts
对对象做是否是简单对象的判断。凡不是new Object()或者字面量的方式构建出来的对象都不是简单对象
所谓的简单对象就是该对象的__proto__等于Object.prototype
即 ={}
或 new Object()
出来的对象才是简单对象。
export default function isPlainObject(obj: any): boolean {
if(typeof obj !== 'object' || obj === null) return null;
let proto = obj;
if(Object.getPrototypeOf(proto) !== null) {
proto = Object.getPrototypeOf(proto);
}
return proto === Object.getPrototypeOf(obj)
}
二、src
1、index.ts
首页就是 redux
的主入口,结尾抛出5种我们常用的方法。在 index.ts
中,只有一个判断函数,我们来看下:
防止在开发环境中对代码惊醒压缩。
function isCrushed() {
if(
process.env.NODE.DEV !== 'product' &&
typeof isCrushed.name === 'string' &&
isCrushed.name === 'isCrushed';
) {
warning();
}
}
如果是在开发环境中对代码进行压缩的话,isCrushed.name 会为 isCrushed
。
2、createStore.ts
createStore
可接受三个参数(reducer, preloadedState, enhancer)
一般 preloadedState
, 我们用的比较少先不说。第三个参数为增强器。
浓缩:1个操作,4个判断,5个赋值,6个方法。
-
判断是否第二、三个参数都是方法
-
判断是否第三个参数未传递,第二个参数传递进来的是方法
-
增强器一定要为方法
-
reducer 一定要为方法
-
getState()
-
dispatch(action)
-
ensureCanMutateNextListeners()
-
subscribe(listener)
-
replaceReducer()
-
observable()
4个判断其一:
if(
(typeof preloadedState === 'function' && typeof enhancer === `function`) ||
(typeof enhancer === 'function' && typeof arguments[3] === 'function')
) {
warning('如果你第二第三个参数传递的都是方法,那么这两个参数会被当作是增强器,而发出警告,提示要将增强器的数据放到一个函数里传递进来')
}
4个判断其二:
将第二个参数赋值给第三个参数,将第二个参数设置为 undefinded
if(typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState;
preloadedState = undefined;
}
4个判断其三:
if(typeof enhancer !== 'undefined') {
if(typeof enhancer !== 'function') {
warning('增强器一定要为方法');
}
return enhancer(createStore)(reducer, preloadedState)
}
4个判断其四:
if(typeof reducer !== 'function')) { warning('reducer 一定要为方法') }
5个赋值:
- 重新赋值一遍 reducer
- 重新赋值一遍 第二个参数 state
- 创建监听者列表
- 浅拷贝一份监听者列表
- 锁。若仓库的数据源在修改中,则其他
action
将不能进行操作,每次修改只能一个。
let currentReducer = reducer;
let currentState = preloadState;
let currentListeners: (() => void)[] | null = [];
let nextListeners = currentListeners;
let isDispatching = false;
6个方法其一:getState()
function getState() {
if(!isDispatching) {
return currentState;
}
}
这里我们看到 getState()
并没有对数据源进行浅拷贝,而是直接返回给我们,所以我们是可能对获取到数据直接修改的。但是修改后不会通知到其他的监听器。
6个方法其二:dispatch(action)
首先先做三个判断:1、是否是简单函数 2、是否类型未定义 3、是否锁开着
function dispatch(action: A) {
if(!isPlainObject(action)) throw new Error('');
if(typeof action.type === `undefined`) throw new Error('');
if(isDispatching) throw new Error('');
// 锁起来,执行仓库数据源修改操作,放开锁
try {
isDispatching = true;
currentState = currentReducer(currentState, action);
} finally {
isDispatching = false;
}
// 通知一个个订阅者
let listeners = (currentListeners = nextListeners);
for(let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
listener();
}
return action;
}
6个方法其三:ensureCanMutateNextListeners()
对监听者的一个浅拷贝,防止用户在调度时调用 subscribe/unsubscribe
会出错误。
function ensureCanMutateNextListeners() {
if(nextListeners === currentListeners) {
nextListeners = currentListeners.slice();
}
}
6个方法其四:subscribe(listener)
注册订阅者之前,首先先做两个判断:1、监听对象是否是个方法 2、仓库是否锁着
function subscribe(listener: () => {}) {
if(typeof listener === 'function') throw new Error('');
if(isDispatching) throw new Error('');
let isSubscribed = true;
ensureCanMutateNextListeners();
nextListeners.push(listener);
return function unsubscribe() {
if(!isSubscribed) return ;
if(isDispatching) throw new Error('');
isSubscribed = false;
ensureCanMutateNextListeners();
// 这里为什么要删除呢,因为取消监听了呀
const index = nextListeners.indexOf(listener);
nextListeners.splice(index, 1);
currentListeners = null;
}
}
6个方法其五:replaceReducer(nextReducer)
该方法是用来替换 reducer
的,执行操作前,先判断一下 nextReducer
是否是方法
function replaceReducer(nextReducer) {
if(typeof nextReducer !== 'function') throw new Error('');
currentReducer = nextReducer;
dispatch({ type: ActionTypes.REPLACE })
}
6个方法其六: observable()
暂不做解释
1个操作
dispatch({ type: ActionTypes.INIT })
createStore
方法里的唯一一个操作。做什么呢?
用来初始化整个 store
的数据结构, 获取 reducer
的默认数据,并结合 combine
, 生成仓库的初始数据源
如果没有该行代码,仓库中的数据源就为 undefined
,无法进行接下来的操作。
下一篇文章请小伙伴们移步至 Redux 源码共读 -- 2