最近在公司内部培训的时候,发现很多小伙伴只是会用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. C# 6 与 .NET Core 1.0 高级编程 - 38 章 实体框架核心(上)

    译文,个人原创,转载请注明出处(C# 6 与 .NET Core 1.0 高级编程 - 38 章 实体框架核心(上)),不对的地方欢迎指出与交流. 章节出自<Professional C# 6 ...

  2. MVC分页控件

    1.下载MvcPager.dll文件并引用到MVC项目中 2.在控制器中引用命名空间using Webdiyer.WebControls.Mvc; 3.获取数据集合,数据的总记录数(数据集合有多少条数 ...

  3. ios 学习动画的套路 (一)

    你也肯定喜欢炫酷的动画! 在APP中,动画就是一个点睛之笔!可以给用户增加一些独特的体验感,估计也有许多的和我一样的,看着那些觉得不错的动画,也就只能流口水的孩子,毕竟~不知道从哪里下手去写!会连续的 ...

  4. vue实现图书管理demo

    年后公司的项目要求用到vue.js知识,我angular没有学,node.js和react也只是了解了一点点,所以学起来比较困难.如果你想学vue.js的知识,推荐网址:http://vuejs.or ...

  5. Bootstrap记录

    左侧 导航下拉: <li class="dropdown"> <a href="#" class="dropdown-toggle& ...

  6. Vim进阶命令

    1. 查找    /xxx(?xxx)       表示在整篇文档中搜索匹配xxx的字符串, / 表示向下查找, ? 表示                    向上查找.其中xxx可以是正规表达式, ...

  7. 前端学PHP之正则表达式基础语法

    前面的话 正则表达式是用于描述字符排列和匹配模式的一种语法规则.它主要用于字符串的模式分割.匹配.查找及替换操作.在PHP中,正则表达式一般是由正规字符和一些特殊字符(类似于通配符)联合构成的一个文本 ...

  8. Html5与CSS3权威指南 百度云下载

    Html5与CSS3权威指南 百度云下载 链接:http://pan.baidu.com/s/1hq6Dlvm 密码:php3

  9. javascript中加var和不加var的区别

    Javascript是遵循ECMAScript标准下的一个产物,自然ECMAScript的标准其要遵循. 先来看下var关键字的定义和用法 var 语句用于声明变量. JavaScript 变量的创建 ...

  10. iOS 图片旋转方法

    iOS 图片旋转方法 通过 CGImage 或 CIImage 旋转特定角度 UIImage可通过CGImage或CIImage初始化,初始化方法分别为init(cgImage: CGImage, s ...