Redux 源码共读 -- 1

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

记的双击么么哒~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值