解读只有99行的Redux(一)—— 从创建一个store开始

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结构的运算,它只是根据传入的状态数据stateaction来判断返回一个新的statereducer必须是一个纯函数,纯函数主要的含义就是它不可以修改影响输入值,并且没有副作用,副作用指的是例如函数中一些异步调用或者会影响函数作用域之外的变量一类的操作。
  另外,注意我们返回新的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内部会调用reducercurrentReducer)并利用传入的actionstore内部的状态数据(currentState)进行处理,但是这个过程可能是比较耗时的,所以为了避免对正在进行reducer处理的store再次进行reducer处理,专门用isDispatching来标识当前store内部是否正在进行reducer处理,如果isDispatchingtrue的话,就不会再对store进行reducer处理,起到一个加锁的作用。
  所以disptach内部会首先检测isDispatching是否正处于一个锁死的状态,如果isDispatchingtrue,说明锁死,正在进行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系列的其他两篇文章

  解读只有99行的Redux(二) 中间件相关
  解读只有99行的Redux(三) 辅助函数和组合Reducer

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容