概述

  • 一个状态管理工具
  • Store:保存数据的地方,你可以把它看成一个容器,整个应用只能有一个 Store。
  • State:包含所有数据,如果想得到某个时点的数据,就要对 Store 生成快照,这种时点的数据集合,就叫做 State。
  • Action:Action 就是 View 发出的通知,表示 State 应该要发生变化了。
  • Action Creator:View 要发送多少种消息,就会有多少种 Action。如果都手写,会很麻烦,所以我们定义一个函数来生成 Action,这个函数就叫 Action Creator。
  • Reducer:Store 收到 Action 以后,必须给出一个新的 State。这种 State 的计算过程就叫做 Reducer。Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。
  • dispatch:是 View 发出 Action 的唯一方法。

整个工作流程:

  1. 首先,用户(通过 View)发出 Action,发出方式就用到了 dispatch 方法。
  2. 然后,Store 自动调用 Reducer,并且传入两个参数:当前 State 和收到的 Action,Reducer 会返回新的 State
  3. State 一旦有变化,Store 就会调用监听函数,来更新 View。

三大原则

1.单一数据源(Store) 整个应用的State被存放在一棵Object tree中,并且这个Object tree只存在唯一一个Store中;

2.State是只读的 唯一改变 State 的方法就是触发 Action,Action 是一个用于描述已发生事件的普通对象。 确保了所有的修改都能被集中化处理。

3.通过纯函数Reducer来修改Store, Reducer 只是一些纯函数,它接收先前的 State 和 Action,并返回新的 State。 即reducer(state, action) => new state

createStore创建store

  • createStore 方法接受 3 个参数参数 (reducer, [preloadedState], enhancer);

    返回 store,store 上挂载着 dispatch、getState、subscribe、replaceReducer 等方法
  • 第二个参数是 preloadedState,它是 state 的初始值,实际上他并不仅仅是扮演着一个 initialState 的角色,如果我们第二个参数是函数类型,createStore 会认为传入了一个 enhancer,如果我们传入了一个 enhancer,createStore 会返回 enhancer(createStore)(reducer, preloadedState)的调用结果,这是常见高阶函数的调用方式。
  • enhancer 接受 createStore 作为参数,对 createStore 的能力进行增强,并返回增强后的 createStore。然后再将 reducer 和 preloadedState 作为参数传给增强后的 createStore,最终得到生成的 store。
export default function createStore(reducer, preloadedState, enhancer) {
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
// 第二个参数是一个函数,没有第三个参数的情况
enhancer = preloadedState;
preloadedState = undefined;
}
// 如果第三个参数是函数走下面的逻辑,返回一个新的createStore
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
// enhancer 不是函数就报错
throw new Error('Expected the enhancer to be a function.');
}
// enhancer就是高阶函数,强化了本身这个createStore的函数,拿到增强后的createStore函数去处理
// applyMiddleware这个函数还会涉及到这个
return enhancer(createStore)(reducer, preloadedState);
}
if (typeof reducer !== 'function') {
// reducer不是函数报错
throw new Error('Expected the reducer to be a function.');
}
// 其他代码省略
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable,
};
}

applyMiddleware 应用中间件

  • 返回一个函数 enhancer;
  • 右边中间件的执行时机由左边的中间件决定,这是因为 next 的方法的调用时机由右边控制
  • dispatch 的处理使用了闭包,这样保证在中间件中调用 dispatch 也是用的最终的 dispatch,也是同一个 dispatch;
  • 写中间件的时候如果要调用 dispatch,一定要有跳出条件,防止死循环
export default function applyMiddleware(...middlewares) {
return (createStore) =>
(...args) => {
const store = createStore(...args);
let dispatch = () => {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.',
);
}; const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args),
};
const chain = middlewares.map((middleware) => middleware(middlewareAPI)); dispatch = compose(...chain)(store.dispatch);
return {
...store,
dispatch,
};
};
}
// 其实就是修改了dispatch
let store = applyMiddleware(middleware1,middleware2)(createStore)(rootReducer);

combineReducers 合并多个reducer

从执行结果看,这时候 state 已经变成了一个以这些 reducer 为 key 的对象;reducer 也变成了一个合并的 reducer;

遍历执行所有的 reducer 的时候把 action 传进去,返回新的 state;

export default 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);
/* 返回一个整合后的reducers */
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;
};
}

dispatch

  • 默认的 action 只能是普通对象,除非使用了第三方中间件,比如 redux-thunk
function dispatch(action) {
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.',
);
} if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?',
);
} if (isDispatching) {
throw new Error('Reducers may not dispatch actions.');
}
try {
isDispatching = true;
currentState = currentReducer(currentState, action);
} finally {
isDispatching = false;
} var listeners = (currentListeners = nextListeners); for (var i = 0; i < listeners.length; i++) {
var listener = listeners[i];
listener();
}
return action;
}

中间件

  • Redux 中间件在发起一个 action 至 action 到达 reducer 的之间提供了一个第三方的扩展。本质上通过插件的形式,将原本的 action->redux 的流程改变为 action->middleware1->middleware2-> ... ->reducer,通过改变数据流,从而实现例如异步 Action、日志输入的功能。
  • Redux 中间件范式
    • 一个中间件接收 store 作为参数,会返回一个函数
    • 返回的这个函数接收老的 dispatch 函数作为参数(一般用 next 作为形参),会返回一个新的函数
    • 返回的新函数就是新的 dispatch 函数,这个函数里面可以拿到外面两层传进来的 store 和老 dispatch 函数
function logger(store) {
return function (next) {
return function (action) { // 新的 dispatch 函数
console.group(action.type);
console.info('dispatching', action);
let result = next(action);
console.log('next state', store.getState());
console.groupEnd();
return result;
};
};
}

中间件的调用顺序

  • 首先调用顺序和书写顺序是一致的,但是这里面的洋葱模型包含了两次顺序,从洋葱出来的顺序是和书写顺序相反
import React from 'react';
import { createStore, applyMiddleware } from 'redux';
function createLogger({ getState, dispatch }) {
return (next) => (action) => {
const prevState = getState();
console.log('createLogger1');
const returnValue = next(action);
const nextState = getState();
const actionType = String(action.type);
const message = `action ${actionType}`; console.log(`%c prev state`, `color: #9E9E9E`, prevState);
console.log(`%c action`, `color: #03A9F4`, action);
console.log(`%c next state`, `color: #4CAF50`, nextState);
return returnValue;
};
} function createLogger2({ getState }) {
return (next) => (action) => {
const console = window.console;
const prevState = getState();
console.log('createLogger2');
const returnValue = next(action);
const nextState = getState();
const actionType = String(action.type);
const message = `action ${actionType}`; console.log(`%c prev state2`, `color: #9E9E9E`, prevState);
console.log(`%c action2`, `color: #03A9F4`, action);
console.log(`%c next state2`, `color: #4CAF50`, nextState);
return returnValue;
};
}
const reducer = function (state = { number: 0 }, action) {
switch (action.type) {
case 'add':
return {
number: state.number + action.number,
};
default:
return state;
}
}; const store = createStore(
reducer,
applyMiddleware(createLogger, createLogger2),
);
store.subscribe(function () {
console.log(1111);
});
const { dispatch } = store;
const App = () => {
const handler = () => {
dispatch({ type: 'add', number: 10 });
};
return (
<div>
<button onClick={handler}>触发redux</button>
</div>
);
};
export default App;

store

store 的属性如下:

  1. dispatch: ƒ dispatch(action)
  2. getState: ƒ getState()
  3. replaceReducer: ƒ replaceReducer(nextReducer)
  4. subscribe: ƒ subscribe(listener)

redux 数据流

Redux 的数据流是这样的:

界面 => action => reducer => store => react => virtual dom => 界面

bindActionCreators

将action对象转为一个带dispatch的方法

比如connect接收的mapDispatchToProps 是对象,会使用 bindActionCreators 处理; 接收 actionCreator 和 dispatch,返回一个函数;

function bindActionCreator(actionCreator, dispatch) {
// 返回一个函数
return function() {
return dispatch(actionCreator.apply(this, arguments))
}
}
function bindActionCreators(actionCreators, dispatch) {
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
const boundActionCreators = {}
for (const key in actionCreators) {
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
} const mapDispatchToProps = { // actionCreators 这是个集合,
onClick: (filter) => {
type: 'SET_VISIBILITY_FILTER',
filter: filter
};
} 转换为:
const mapDispatchToProps = { // actionCreators 这是个集合,
onClick:function(filter) {
return dispatch({ // dispatch 是闭包中的方法
type: 'SET_VISIBILITY_FILTER',
filter: filter
})
}
}

compose

函数套函数,compose(...chain)(store.dispatch)结果返回一个加强了的 dispatch;

这点和koa比较相似,这个 dispatch 在执行的时候会调用中间件。

function compose(...funcs) {
if (funcs.length === 0) {
return (arg) => arg;
} if (funcs.length === 1) {
return funcs[0];
}
// 每一次reduce迭代都会返回一个加强版的dispatch
return funcs.reduce(
(a, b) =>
(...args) =>
a(b(...args)),
);
}

加强版 dispatch(一个方法,接收 action 参数),在中间件中用 next 表示,执行 next 之后,会形成一个链条。

enhancer

  • enhancer,翻译过来就是 store 加强器,比如 applymiddleware 的返回值就是一个 enhaner。store 加强器可以重新构建一个更强大的 store,来替换之前的基础版的 store,让你的程序可以增加很多别的功能,比如 appllymiddleware 可以给你的 redux 增加中间件,使之可以拥有异步功能,日志功能等!
// 以createStore为参数
(createStore) =>
(...args) => {};

使用 redux 常遇见的问题

  • 样板代码过多 增加一个 action 往往需要同时定义相应的 actionType 然后再写相关的 reducer。例如当添加一个异步加载事件时,需要同时定义加载中、加载失败以及加载完成三个 actionType,需要一个相对应的 reducer 通过 switch 分支来处理对应的 actionType,冗余代码过多;

    目前已经存在着非常多的解决方案,比如dva redux-tookit等。

  • 更新效率问题:由于使用不可变数据模式,每次更新 state 都需要拷贝一份完整的 state 造成了内存的浪费以及性能的损耗。

    其实 redux 以及 react-redux 中内部已做优化,开发的时候使用 shouldComponentUpdate 等优化方法也可以应用,也可以用不可变数据结构如 immutable、Immr 等来解决拷贝开销问题。

redux原理分享的更多相关文章

  1. CocoaPods 原理分享及遇到的问题改进

    cocoapods 原理分享及问题阐述 cocoapods 管理私有工程,需要两个git 仓库, repo 仓库,保存podspec 文件,告诉我们项目从哪来, 项目 仓库,保存工程文件,告诉我们引用 ...

  2. 轻松理解Redux原理及工作流程

    轻松理解Redux原理及工作流程 Redux由Dan Abramov在2015年创建的科技术语.是受2014年Facebook的Flux架构以及函数式编程语言Elm启发.很快,Redux因其简单易学体 ...

  3. redux原理

    Redux实现原理 不同组件需要依赖同一个数据的时候,就需要状态提升至这些组件的根组件. redux是状态统一管理工具,需要使用它的原因是: 组件之间通信统一管理,方便代码维护. React中有一个特 ...

  4. [转载]Redux原理(一):Store实现分析

    写在前面 写React也有段时间了,一直也是用Redux管理数据流,最近正好有时间分析下源码,一方面希望对Redux有一些理论上的认识:另一方面也学习下框架编程的思维方式. Redux如何管理stat ...

  5. Redux原理(一):Store实现分析

    写在前面 写React也有段时间了,一直也是用Redux管理数据流,最近正好有时间分析下源码,一方面希望对Redux有一些理论上的认识:另一方面也学习下框架编程的思维方式. Redux如何管理stat ...

  6. 解析:让你弄懂redux原理

    作者: HerryLo 本文永久有效链接: https://github.com/AttemptWeb...... Redux是JavaScript状态容器,提供可预测化的状态管理. 在实际开发中,常 ...

  7. Socket 核心原理分享

    Socket 的个人故事.希望通过这篇文章让你弄懂什么是 Socket,明白 TCP 和 UDP 协议的通讯,明白长连接和短连接的优缺点,明白 BIO.NIO.AIO的区别. Socket 大家好,我 ...

  8. Redux 原理和简单实现

    前端开发中React + Redux 是大部分项目的标配,Redux也是我喜欢的库之一,他的源码也拜读过几遍,每次都有很多收获,尤其他的中间件设计模式,对自己封装一些库提供了思想上的指导. Redux ...

  9. react-redux原理分析

    写在前面 之前写了一篇分析Redux中Store实现的文章(详见:Redux原理(一):Store实现分析),突然意识到,其实React与Redux并没有什么直接的联系.Redux作为一个通用模块,主 ...

  10. React + Redux 入坑指南

    Redux 原理 1. 单一数据源 all states ==>Store 随着组件的复杂度上升(包括交互逻辑和业务逻辑),数据来源逐渐混乱,导致组件内部数据调用十分复杂,会产生数据冗余或者混用 ...

随机推荐

  1. SQL语句中过滤条件放在on和where子句中的区别和联系

    摘要: 介绍在多表关联SQL语句中,过滤条件放在on和where子句中的区别--inner join中没区别,外连接就不一样. 综述   蚂蚁金服的一道SQL面试题如下:SQL语句中,过滤条件放在on ...

  2. 【微服务】- Nacos - 注册中心

    微服务 - 注册中心 - Nacos 生命不息,写作不止 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! 上一 ...

  3. Elasticsearch:Dynamic mapping

    Elasticsearch最重要的功能之一是它试图摆脱你的方式,让你尽快开始探索你的数据. 要索引文档,您不必首先创建索引,定义映射类型和定义字段 - 您只需索引文档,那么index,type和fie ...

  4. Elasticsearch:正确使用regexp搜索

  5. 内网横向渗透 之 ATT&CK系列一 之 拿下域控制器

    信息收集 信息收集 域控制器的相关信息: 通过arp扫描发现域控制器的ip地址为:192.168.52.138,尝试使用msf的smb_login模块登录smb是否成功 1 search smb_lo ...

  6. Linux+Wine玩火影忍者究极风暴3指南

    如果你的系统没有Wine先装Wine,Wine在各大发行版的源都能找到.记住32位和64位的Wine都要装 去https://www.playonlinux.com/wine/binaries/pho ...

  7. 测试杂谈——一条SQL引发的思考(二)

    在前段时间,曾写过一篇关于SQL问题的文章,测试杂谈--一条SQL引发的思考(一). 今天这篇,算是个问题记录吧,问题并不复杂,但对于测试同学而言,确实是个需要关注的点. 问题分析 最近在日常工作中, ...

  8. 后端框架的学习----mybatis框架(6、日志)

    六.日志 如果一个数据库操作,出现了异常,我们需要排错,日志就是最好的帮手 setting设置 <settings> <setting name="logImpl" ...

  9. 创建第一个springboot项目、用springboot实现页面跳转、@Controller和@RestController的区别

    文章目录 一.第一个spring boot项目 二.spring boot跳转到指定页面 三.怎样将后台的信息传递到前台 四. @Controller和@RestController的区别? 一.第一 ...

  10. 安装notepad++ 安装Python Python环境变量的数值。怎样在notepad++上运行Python的代码

    文章目录 1.下载安装一个Python的编辑器notepad++,(我这里有现成的,也可以去网上搜很多) 2.安装python,(我这里有现成的,也可以去网上下载). 3.怎样彻底删除Python,有 ...