揭秘redux工作原理及源码解读
- 这是本人第一次写技术文档,也是蛮紧张的,如果有不足之处还请指教。
- 该文章主要分析redux理念,
中间件
机制,enhancer
插件机制及工作原理,以及redux
,redux-saga
,react-redux
部分源码解读,通过该文章你将会收获Flux架构
理念,函数式编程,以及redux在react中的工作方式。 - 本文章适合有一定redux基础,或者使用过redux的童鞋,如果你对redux还不是很熟,可以先阅读官方文档了解redux.
- Flux是什么
- 简单来Flux是一种架构理念,处理动态数据流的一种方案。它采用单向数据流的方式控制用户行为,数据处理,以及视图展示,让动态数据的处理变得更加简单和清晰。
- Flux的结构
- Flux主要由4部分组成
视图
View: 展示给用户的界面行为
Action: 描述事件类型和内容派发
Dispatch: 用户触发行为的唯一方式数据
Store: 储存数据的仓库
- Flux主要由4部分组成
- 什么是redux
- redux是前端管理数据的JavaScript库,借鉴Flux思想,并受到了Elm语言的启发,它的体积极小(核心源码不足100行),其精妙的函数式理念,清晰的结构,受到了前端开发者的一致追捧,接来下我们将先从结构入手,一步一步揭开redux的神秘面纱。
- redux的结构
- Redux的核心结构主要由
state
,action
,dispatch
,reducer
,subscribe
组成.- state
- 存储数据的对象
- action
- 事件行为
- dispatch
- 用户触发行为的唯一方式
- reducer
- 接受action,并处理state,返回一个新的state
- subscribe
- 订阅事件,监听数据变化,当数据变化时触发订阅时间
- state
- Redux的核心结构主要由
- vue和react是如何监听数据的变化的?
- vue监听数据的变化
- 在Vue中用到了Object.definePropertyAPI去劫持数据,当我们尝试修改该属性时,会触发set函数,从而发起一次update()
let value; const obj = {}; Object.defineProperty(obj, "value", { get() { return value; }, set(newValue) { // 当设置的值跟原值一样,直接return if (newValue === value) { return; } else { // 当时设置值不一样表示数据发生了变化,触发update value = newValue; // update() console.log(`我已经监听到object.value的变化, 并将新的值${newValue}赋值给obj.value`); } }, }); obj.value = 3; // 触发更新 obj.value = 3; obj.value = 4; // 触发更新
- 在Vue中用到了Object.definePropertyAPI去劫持数据,当我们尝试修改该属性时,会触发set函数,从而发起一次update()
- react监听数据变化的方式(react Hook为例)
- react采用的
state
,setState
的方式监听数据,如果直接给state赋值是无效的,我们简单那useState
来举例.function useState(initState) { let state = initState || undefined; const setState = (newValue) => { state = newValue; console.log(`已监听数据变化, 设置新的state为${newValue}`); } return [state, setState]; } const [state, setState, getState] = useState(1); setState(2) // 已监听数据变化, 设置新的state为2 setState(3) // 已监听数据变化, 设置新的state为2 console.log(state); // 1
- 注:上方的
useState
代码属于阉割版的,我们只借用了React.useState
的部分结构,如果想了解React hooks
源码的童鞋可以联系我,我可以后续再写一期关于React hooks
源码的文章。
- 注:上方的
- 两者的区别
- 从本质上实现方式同步,导致了
Vue
(双向绑定),与React
(单向数据流)的不同数据流特征。 - Vue可以同步获取数据,而
React
获取数据的方式是异步的。
- 从本质上实现方式同步,导致了
- 分析:在上面的代码中,我们注意到
console.log(state)
并没有获取到新set的数据,useState
函数执行完成的时候,return
的数据始终被拉在外层作用域,其实问题就出在state = newValue
,当newValue
直接将整个数据替换成了state
,如果原state
是引用数据类型,那么它的内存地址也将改变,而原先返回的return数据依然是之前的引用。基于上面的案列,我们不能同步拿到当前state
的数据,那么有没有其他方式同步拿到新的state
呢?请看下面的代码function useState(initState) { let state = initState || undefined; const setState = (newValue) => { state = newValue; console.log(`已监听数据变化, 设置新的state为${newValue}`); } //这里新增了一个getState const getState = () => { // ++++ return state } return [ state, setState, getState // ++++ 并将getState返回 ] } const [state, setState, getState] = useState(1); setState(2) setState(3) console.log(state); console.log(getState()); // ++++ 当我们调用getState方式时,会直接从函数内部的state获取数据
- react采用的
- vue监听数据的变化
- createStore 创建redux仓库
- 为什么redux只能有一个仓库
- 在redux官方文档中对
createStore
有这样的描述。Creates a Redux store that holds the complete state tree of your app. There should only be a single store in your app. - 对于这个问题,我一直想不明白为什么一个应用中只能拥有一个仓库。比如在
react-keep-alive
组件中就用到了redux,而我们自己也可以在全局创建Store,在单独的自模块中也可以创建store.且createStore本身在创建时就可以创建独立的闭包作用域去单独管理他们各自的状态。带着疑惑,我咨询了专业的大佬,于是得到了这样的结论。 - redux的目的就是实现应用
中央状态的集中管理
,这种违背了redux理念的初衷。当单个应用变得复杂时,建议使用combineReducer
去拆分模块,而不是新建仓库。但是我们可以,在不同的应用中创建不同的store,然后去独立管理。
- 在redux官方文档中对
- createStore结构
- 我们先来通过简化后的源码分析一下create结构
通过上述代码,我们可以清晰的看到createStore一共接受三个参数,
function createStore(reducer, preloadedState, enhancer) { // 省略中间代码 return { dispatch, subscribe, getState, } }
reducer
,preloadedState
,enhancer
,并返回了dispatch
,subscribe
,getState
等方法。
- 我们先来通过简化后的源码分析一下create结构
- 为什么redux只能有一个仓库