最近在公司内部培训的时候,发现很多小伙伴只是会用redux、react-redux、redux-thunk的api,对于其中的实现原理和数据真正的流向不是特别的清楚,知其然,也要知其所以然,其实redux的源代码非常简介,下面逐一介绍,

1.先看一个简单的redux应用的例子:

import { createStore, combineReducers } from 'redux';

const year = (state, action) => {
let defaultState = {
year: 2017
}
state = state || defaultState;
switch (action.type) {
case 'add':
return {
year: state.year + 1
};
break;
case 'sub':
return {
year: state.year - 1
};
break;
default:
return state;
break;
}
} const count = (state, action) => {
let defaultState = {
count: 1
}
state = state || defaultState;
switch (action.type) {
case 'addone':
return {
count: state.count + 1
};
break;
case 'subone':
return {
count: state.count - 1
};
break;
default:
return state;
break;
}
} const reducer = combineReducers({
year: year,
count: count
}) const store = createStore(reducer); store.subscribe(() => {
console.log('the year is ' + store.getState().year.year);
});
store.subscribe(() => {
console.log('数字是' + store.getState().count.count);
}); const action1 = {
type: 'add'
}
const action2 = {
type: 'addone'
}
const action3 = {
type: 'hello'
} store.dispatch(action1);
store.dispatch(action2);
store.dispatch(action3);

上面代码执行后打印出来的结果是:

想必大部分小伙伴都觉得很简单。

2.从上面的实例可以看到redux的核心内容就是:
  A.createStore方法以及执行之后返回的store对象,定义在store对象上的store.subscribe()监听某个函数,store.getState()获取当前store中state(一个复杂的对象)的值,store.dispatch()推送某个action
  B.combineReducers将多个reducer合并成一个rootReducer,本质上为合并对象,并返回一个新的总的的reducer函数
2.1 createStore 方法简介
     createStore方法内部根据传入的三个参数(reducer、初始状态、增强者),在内部创建了几个核心函数,然后把这几个函数放到一个对象里面返回,也就是我们示例代码中常用到的store对象

export default function createStore(reducer, preloadedState, enhancer) {
function getState() {}
function subscribe(listener) {}
function dispatch(action) {}
function replaceReducer(nextReducer) {}
...
return {
dispatch,
subscribe,
getState,
replaceReducer,
...
} }

2.1.1  subscribe方法的核心代码,就是把一个一个的监听函数放入到Listeners的数组,然后返回一个unsubscribe函数,一个闭包函数,包含着传入的listeners函数,执行该函数从监听数组中移除该listeners

function subscribe(listener) {
if (typeof listener !== 'function') {
throw new Error('Expected listener to be a function.')
} let isSubscribed = true ensureCanMutateNextListeners()
nextListeners.push(listener) return function unsubscribe() {
if (!isSubscribed) {
return
} isSubscribed = false ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
}
}

2.1.2  getState 直接返回当前的state的值,如果没有进行任何操作,直接返回默认值,如果是经过dispatch(action)之后,在dispatch中触发了reducer函数,生成了新的state,也是直接返回

function getState() {
return currentState
}

2.1.3 dispatch store上的最核心方法, 两部分组成,第一部分直接把当前的state和传入的action直接传入reducer函数,执行,生成新的state,供getState使用,第二部分是直接循环执行subscribe中添加到listeners数组中的监听函数,也就是触发监听函数,

逻辑非常简单。

function dispatch(action) {
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
} const listeners = currentListeners = nextListeners
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
} return action
}

2.2  combineReducers,将多个reducer合并成一个rootReducer,本质上为合并对象,并返回一个新的总的的reducer函数

function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers)
const finalReducers = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
const finalReducerKeys = Object.keys(finalReducers)
return function combination(state = {}, action) {
let hasChanged = false
const nextState = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
const nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
}
}

combineReducers的一个简单实现方式,更能明白其中的工作原理:

const combineReducers = (reducers) => {
return (state = {}, action) => {
return Object.keys(reducers).reduce(
(nextState, key) => {
nextState[key] = reducers[key](
state[key],
action);
return nextState;
},
{})
}
}

2.3 applyMiddleware 中间件,对dispatch的增强,一般为添加异步操作等,例如redux-thunk中间件,实际上就是如果action是个方法,则执行这个方法,如果不是则正常dispatch(action)

function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => {
const store = createStore(reducer, preloadedState, enhancer)
let dispatch = store.dispatch
let chain = [] const middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch) return {
...store,
dispatch
}
}
} 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;

3.react-redux

3.1通过provider 提供store,react通过Context属性,可以将props直接给子孙component,无须通过props层层传递,Provider仅仅起到获得store, 然后将其传递给子孙元素而已

3.2 connect  这个是最关键的方法,

connect 是一个高阶函数,首先传入mapStateToProps、mapDispatchToProps,然后返回一个生产Component的函数(高阶组件HOC),然后再将真正的component作为参数传给这个函数,这样就生产了一个经过包裹的connect组件,该组件具有以下特点:
    通过this.context获取祖先component的store
    props包括stateProps、dispatchProps、parentProps,合并在一起得到nextState,作为props传给真正的Component
    componentDidMount时,添加事件this.store.subscribe(this.handleChange),实现页面交互
    shouldComponentUpdate时判断是否有避免进行渲染,提升页面性能,并得到nextState
    componentWillUnmount时移除注册的事件this.handleChange
 

redux核心思路和代码解析的更多相关文章

  1. 学习Redux之分析Redux核心代码分析

    1. React,Flux简单介绍 学习React我们知道,React自带View和Controller库,因此,实现过程中不需要其他任何库,也可以独立开发应用.但是,随着应用程序规模的增大,其需要控 ...

  2. java集合框架之java HashMap代码解析

     java集合框架之java HashMap代码解析 文章Java集合框架综述后,具体集合类的代码,首先以既熟悉又陌生的HashMap开始. 源自http://www.codeceo.com/arti ...

  3. Java面试 32个核心必考点完全解析

    目录 课程预习 1.1 课程内容分为三个模块 1.2 换工作面临问题 1.3 课程特色 课时1:技术人职业发展路径 1.1 工程师发展路径 1.2 常见技术岗位划分 1.3 面试岗位选择 1.4 常见 ...

  4. [代码]解析nodejs的require,吃豆人的故事

    最近在项目中需要对nodejs的require关键字做解析,并且替换require里的路径.一开始我希望nodejs既然作为脚本语言,内核提供一个官方的parser库应该是一个稳定可靠又灵活的渠道,然 ...

  5. Beam Search快速理解及代码解析(上)

    Beam Search 简单介绍一下在文本生成任务中常用的解码策略Beam Search(集束搜索). 生成式任务相比普通的分类.tagging等NLP任务会复杂不少.在生成的时候,模型的输出是一个时 ...

  6. Beam Search快速理解及代码解析

    目录 Beam Search快速理解及代码解析(上) Beam Search 贪心搜索 Beam Search Beam Search代码解析 准备初始输入 序列扩展 准备输出 总结 Beam Sea ...

  7. [nRF51822] 12、基础实验代码解析大全 · 实验19 - PWM

    一.PWM概述: PWM(Pulse Width Modulation):脉冲宽度调制技术,通过对一系列脉冲的宽度进行调制,来等效地获得所需要波形. PWM 的几个基本概念: 1) 占空比:占空比是指 ...

  8. MYSQL常见出错mysql_errno()代码解析

    如题,今天遇到怎么一个问题, 在理论上代码是不会有问题的,但是还是报了如上的错误,把sql打印出來放到DB中却可以正常执行.真是郁闷,在百度里面 渡 了很久没有相关的解释,到时找到几个没有人回复的 & ...

  9. 【原创】大数据基础之Spark(5)Shuffle实现原理及代码解析

    一 简介 Shuffle,简而言之,就是对数据进行重新分区,其中会涉及大量的网络io和磁盘io,为什么需要shuffle,以词频统计reduceByKey过程为例, serverA:partition ...

随机推荐

  1. 初识PHP遗留下来的问题?

    待解决的问题: 1.写一个PHP脚本,显示用户输入的名称. 提示: <?php echo $_POST["username"];?>! <?php echo &q ...

  2. maven 配置安装

    1.下载maven http://maven.apache.org/   2.windows安装maven 解压包后配置环境变量 PATH:%M2_HOME%\bin M2_HOME:D:\soft\ ...

  3. 矢量切片(Vector tile)

    说明:本月的主要工作都是围绕制作矢量切片这一个核心问题进行的,所以2月的主题就以这个问题为主,目前分支出来的一些内容主要包括了TMS(Tile map service),OpenLayers3中的Pr ...

  4. wcf ServiceContract

    ServiceContract是什么 ServiceContract怎么用

  5. Jenkins添加用户

    新建用户 Jenkins刚开始的界面是允许访客进行所有操作的,这时Jenkins是有安全隐患的,也不容易去管理.这时,我们需要管理Jenkins的权限,对它的权限进行设置.关于Jenkins权限设置的 ...

  6. 用《内网穿山甲》把本地IIS中的站点共享到远程访问

    前言: 因为各种原因,我们常常要把本机或局域网中搭建的站点发给远方的人访问,他有可能是测试人员.客户.前端.或领导演示,或是内部系统内部论坛临时需要在远程访问,事件变得很麻烦,要么有公网IP,要么能控 ...

  7. JAVA函数的参数传递

    JAVA开发过程中写函数都是不可避免的于是乎参数传递经常会困扰我们,特别是有C/C++功底的童鞋总会纠结于"java到底是值传递还是引用传递?" 先来一段代码(和程序员交流最好的果 ...

  8. DLL 导出变量

    声明为导出变量时,同样有两种方法:   第一种是用__declspec进行导出声明 #ifndef _DLL_SAMPLE_H #define _DLL_SAMPLE_H // 如果定义了C++编译器 ...

  9. 虚拟机Centos开机以后,有eth0网卡,但是没有IP,Determine IP information for eth0.. no link present check cable

    Determine IP information for eth0.. no link present check cable 如果你的VMware虚拟机centos6.5使用NAT模式,开机以后,使 ...

  10. 【转】svn cleanup failed–previous operation has not finished; run cleanup if it was interrupted

    svn提交遇到恶心的问题,可能是因为上次cleanup中断后,进入死循环了. 错误如下 解决方法:清空svn的队列 1.下载sqlite3.exe 2.找到你项目的.svn文件,查看是否存在wc.db ...