Open zhoujiamin opened 7 years ago
写的很详细,不错。 就是 错别字 有点多改一下。另外:
但当工程变的复杂的时候,多个组件需要维护 同一个状态时,不借助任何工具,最简单的方法,就是当公共状态提取到组件树最顶端,通过 props往下传递,这样对代码的组织管理维护带来很大的问题。react的诞生就解决了这个问题。
可以 context 传下去。 解决这个问题的不是 redux ,而是 react-redux 的 connect。 redux 的解决根本问题,可以去官网看看。再想想。
那么,为什么要保证reduer为纯函数呢? 为了比较2个js对象是不是完全相同,必须进行深比较。但是深比较在真实的应用当中代价昂贵,因为通常js的对象都很大,同时需要比较的次数很多。因此,state改变就返回新对象,这样用可以通过比较引用就知道state是否正在改变
根本原因不是这个。因为 return 的 state 整个对象都是新的引用。 所以并不是细粒度的引用改变。更重要的优势你可以再想想。
写的不错。针对 enhancer, middleware 等等的用法,可以写一篇进阶篇。
@limerickgds 差点把你当做qiankai了。
redux的动机和要保证reduer为纯函数原因大概是相同的, 让变化可以预测,对于相同的输入,有可预测的输出
但是让输出为新store对象,主要还是对比state的是不是真正改变了。主要是多个reduer的时候。
很赞,看完了,有一些细节点自己之前看的时候都没注意,不过你可以整个github.io来记录学习笔记,那样可读性会好一点哦
@shanruo 其实有一个, 换了个电脑就懒得搭环境了~~~ 哈哈哈哈
redux源码学习
没想到redux源码竟然那么短小。
为什么会出现redux?
在js单页面应用中,js需要管理复杂的state (状态)。由于前端有各种复杂的UI交互,包括ajax等等,都会去改变state。state 在什么时候,由于什么原因,如何变化已然不受控制,想重现问题或者添加新功能就会变得非常困难。
redux可以维护页面的state (状态),让状态变的可以预测,用过特定的方式触发数据改变,强制让所有的状态变化都必须留下一笔记录,这样就可以利用这个来做各种 debug 工具、历史回滚等等。所谓的单向数据流,就是当用户进行操作的时候,会从组件发出一个 action,这个 action 流到 store 里面,触发 store 对状态进行改动,然后 store 又触发组件基于新的状态重新渲染。
redux是怎么做到可预测的
单一数据源,所有数据都是只读的
要想修改数据,必须 dispatch 一个 action 来描述什么发生了
改变当处理 action 时,必须生成一个新的 state,不得直接修改原始对象
redux单向数据流
调用 store.dispatch(action)
store 调用传入的 reducer 函数
根 reducer 应该把多个子 reducer 输出合并成一个单一的 state 树。
store 保存了根 reducer 返回的完整 state 树。
这个新的树就是应用的下一个 state,所有订阅 store.subscribe(listener) 的监听器都将被调用; 监听器里可以调用 store.getState() 获得当前 state。
极简单项数据流图:
redux使用中存在的问题
redux本身非常短小,概念清晰,但把复杂的工作交给了用户,用户的使用比较复杂,以开发效率换取了数据管理。
与vuex进行对比
实际上不应该用redux对比vuex,而应该用redux+react-redux对比vue。 vuex是专门为Vue设计的状态管理库,而Redux和React之间没有关系,Redux通过React-redux与react搭配使用。vuex的基本思想以及api,与redux都非常相似,但是vuex对数据的驱动,试图的更新是通过其响应式原理完成的,与react-redux+redux不同。
本文以源码的目录结构,来记录自己的学习旅程。
简单的思维导图:
源码目录:
index.js
index.js短短几行暴露出几个核心的API。
warning.js
同样是很简单的文件,用于抛出错误
createStore.js
该文件核心:
1)createStore函数接受参数后,返回一个store对象,这个对象对外暴露了dispatch,subscribe,getState,replaceReducer几个函数。
2)为了初始化state,createStore函数内部执行了一次 dispatch({ type: ActionTypes.INIT })
接下来,逐步分析整个文件。 createStore接受3个参数:reducer,preloadedState, enhancer
reducer
reducer是一个函数,接受当前state和action作为参数,返回next state。
redux要求我们输入的reducer必须是纯函数,那么为什么要保证reduer为纯函数呢? (纯函数概念:函数的返回结果只依赖于它的参数,函数执行过程里面没有副作用,副作用是指对外部产生变化)
实际上,也不一定非纯函数不可,只能说纯函数是一种最佳实践。社区中,有一些讨论,参考
preloadedState
初始化的state。可以用于服务端取得初始化的值。在reducers里可以设置初始值
两者同时设置,以preloadedState为准。
enhancer
增强store。是一个组合 store creator 的高阶函数,返回一个新的强化过的 store creator。
createStore函数内部首先做了一些简单的准备操作。
内部变量,不对外暴露
对外暴露getState函数,使得外部通过store.getState()就可以得到state的值
对外暴露subscribe函数,用于注册listener,当dispatch,subscribe注册的listener就会被执行, 返回一个unsubscribe函数,执行这个函数之后,listener将被注销。 ensureCanMutateNextListeners用于切断nextListeners和currentListeners 之间的联系。使得当dispatch时,注册或者或者注销listener,对dispatch不会立即产生影响。
对外暴露dispatch函数
replaceReducer
替换 store 当前用来计算 state 的 reducer。 这是一个高级 API。只有在你需要实现代码分隔,而且需要立即加载一些 reducer 的时候才可能会用到它。在实现 Redux 热加载机制的时候也可能会用到。
observable函数是一个内部函数,没有在得到调用,因此暂时放一边。
最后,在createStore函数内部中执行了
可以知道,当preloadedState为undefined,currentState将会等于currentReducer函数中为state设置的初始值。
combineReducers.js
combineReducers 辅助函数的作用是,把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore,生成store对象。
由于每个reducer在处理action的时候,如果store发生了改变,则返回新的对象。因此,combineReducers.js中,可以对比每个store(子store)的引用,就可以知道这个store是否 真正发生改变。这大大节约了比较成本,要比较2个JS对象是否完全相同,必须进行深比较。但是深比较在真实的应用当中代价昂贵,因为通常js的对象都很大,同时需要比较的次数很多。因此,当state改变,reduer就返回新对象,这样用可以通过比较引用就知道state是否正在改变
compose.js
从右到左来组合多个函数。 compose(f, g, h) 与 (...args) => f(g(h(...args)))相同 常常用来组合中间件
applyMiddleware.js
Middleware 可以让你包装 store 的 dispatch 方法来达到你想要的目的。同时, middleware 还拥有“可组合”这一关键特性。多个 middleware 可以被组合到一起使用,形成 middleware 链。其中,每个 middleware 都不需要关心链中它前后的 middleware 的任何信息。
本质上middlewares就是对store.dispatch进行了包装,在原有功能的基础上增加了别的功能
bindActionCreator.js
把 action creators 转成拥有同名 keys 的对象, 但使用 dispatch 把每个 action creator 包围起来,这样可以直接调用它们。
本质上就是将actionCreator和dispatch的工作集成在一个函数中。
输入actionCreators为单一函数,则返回函数。 输入为对象时,则返回对象,对象的key值与actionCreators的key值相同。 通过调用返回值boundActionCreators(args),可以完成dispath的操作。