文章来源: IMweb前端社区 黄qiong(imweb.io)
IMweb团队正在招聘啦,简历发至jayccchen@tencent.com

Redux 其实是用来帮我们管理状态的一个框架,它暴露给我们四个接口,分别是:

  • createStore
  • combineReducers
  • bindActionCreators
  • applyMiddleware
  • compose

源码系列里会分别对这五个接口进行解析。

Redux 的源码解析系列开篇之前,先来了解一下它的实现思想。

为什么要有dispatch

假设一种场景下,app里每个组件都需要拿到appState的一部分进行渲染。

但是这里存在一个风险就是,谁都可以修改appState的值,换句话说,有一天当appState变了你都不知道是谁改的,所以我们需要有一个管理员来帮我们管理我们的状态,这时候引入了dispatch函数,来专门修改负责数据的修改。

function dispatch (action) {switch (action.type) {case 'UPDATE_TITLE_TEXT':appState.title.text = action.textbreakcase 'UPDATE_TITLE_COLOR':appState.title.color = action.colorbreakdefault:break}
}复制代码

解决问题:
既可以解决组件共享问题,同时不会有数据可以被任意修改的问题。

为什么要有createStore

现在我们有了状态,又有了dispatch,这时候我们需要一个高层管理者store,帮我们管理好他们,这样再用的时候就可以直接store.getState store.dispatch的方式获取和更改组件。

所以我们就有了createStore这个函数帮我们生成store, 然后将getState 跟 dispatch 方法export出去。

function createStore(state, stateChanger) {const getState = () => state;const dispatch = (action) => stateChanger(state, action)return {getState, dispatch}
}复制代码

createStore 接受两个参数,一个是表示app的 state。另外一个是 stateChanger,它来描述应用程序状态会根据 action 发生什么变化,其实就是相当于本节开头的 dispatch 代码里面的内容,我们后来会将它命名为reducer。

但是这里还有一个问题,就是数据发生改变之后,我们都需要手动在重新render一次APP,这时候就需要观察者模式,订阅数据的改变,然后自动调用renderAPP,所以我们的createStore功能又强大啦~

function createStore(state, reducer) {const getState = () => state;const listeners = [];const subscribe = (listener) => {listeners.push(listener)} const dispatch = (action) => {reducer(state, action);// 数据已发生改变就把所有的listener跑一遍listeners.forEach((listener) => {listener()})}return {getState, dispatch, subscribe}
}复制代码

我们就可以这样使用

store.subscribe(() => renderApp(store.getState()))复制代码

由此可以看出,dispatch是一个重要函数,当每一次我们调用dispatch去改变app的状态的时候,它都会同时执行所有的订阅函数。

到这一步,一个APP就已经可以无压力的跑起来啦,最后一步,当然是关注性能,我们这个app 还是有严重性能问题的,因为每一次的dispatch 所有的子组件都会被重新渲染,这当然是不必要的。

所以就需要对reducer产生的前后appState进行一个对比,这就要求reducer必须是一个纯函数,返回的是一个新的object,不能直接更改reducer的参数,这样才能够对比可以通过对比前后的state是否相等,来决定是否render

// reducer用来管理状态变化
function reducer (state, action) {if(!state) {return appState;}switch (action.type) {case 'CHANGE_TITLE':return {...state,title: {...state.title,text: action.text}}case 'CHANGE_CONTENT':return {...state,content: {...state.content,color: action.color}}}
}复制代码
function createStore(state, reducer) {let appState = state;const getState = () => appState;const listeners = [];const subscribe = (listener) => {listeners.push(listener)} const dispatch = (action) => {// 覆盖原先的appStateappState = reducer(state, action);listeners.forEach((listener) => {listener()})}return {getState, dispatch, subscribe}
}复制代码

OK,到这一步,我们的redux就基本完成啦~ 接着改装下我们的reducer,让它有一个初始值,这样我们的createStore就只需要传入一个reducer即可

// reducer用来管理状态变化
function reducer (state, action) {
//设置初始值if(!state) {return appState;}switch (action.type) {case 'CHANGE_TITLE':return {...state,title: {...state.title,text: action.text}}case 'CHANGE_CONTENT':return {...state,content: {...state.content,color: action.color}}}
}复制代码
function createStore (reducer) {let state = nullconst listeners = []const subscribe = (listener) => listeners.push(listener)const getState = () => stateconst dispatch = (action) => {// 可以看到 由于reducer返回的是一个新的object,那在外层,我们就可以对比nextProps跟t his.props 来决定是否渲染state = reducer(state, action)listeners.forEach((listener) => listener())}dispatch({}) // 初始化 statereturn { getState, dispatch, subscribe }
}复制代码

总结以下:createStore里要做三件事

  • getState
  • dispatch
  • subscribe
  • 初始reducer的状态

四个步骤

// 定一个 reducer, 负责管理数据变化还有初始化appState的数据
function reducer (state, action) {/* 初始化 state 和 switch case */
}// 生成 store
const store = createStore(reducer)// 监听数据变化重新渲染页面
store.subscribe(() => renderApp(store.getState()))// 首次渲染页面
renderApp(store.getState()) // 后面可以随意 dispatch 了,页面自动更新
store.dispatch(...)复制代码

我们整个过程就是不断地发现问题,解决问题

1、共享状态 -> dispatch

2、store统一管理 dispatch getState

3、性能优化 --> reducer是一个纯函数

4、最终初始化整个reducer

以上就是redux的大致思想。

参考文档:

huziketang.com/books/react…

转载于:https://juejin.im/post/5a119426f265da430f31b165

Redux 源码解析系列(一) -- Redux的实现思想相关推荐

  1. TiKV 源码解析系列文章(二)raft-rs proposal 示例情景分析

    作者:屈鹏 本文为 TiKV 源码解析系列的第二篇,按照计划首先将为大家介绍 TiKV 依赖的周边库 raft-rs .raft-rs 是 Raft 算法的 Rust 语言实现.Raft 是分布式领域 ...

  2. Tomcat源码解析系列二:Tomcat总体架构

    Tomcat即是一个HTTP服务器,也是一个servlet容器,主要目的就是包装servlet,并对请求响应相应的servlet,纯servlet的web应用似乎很好理解Tomcat是如何装载serv ...

  3. prometheus变量_TiKV 源码解析系列文章(四)Prometheus(下)

    本文为 TiKV 源码解析系列的第四篇,接上篇继续为大家介绍 rust-prometheus.上篇主要介绍了基础知识以及最基本的几个指标的内部工作机制,本篇会进一步介绍更多高级功能的实现原理. 与上篇 ...

  4. TiKV 源码解析系列 - Raft 的优化

    这篇文章转载TiDB大牛 唐刘 的博客:https://mp.weixin.qq.com/s?__biz=MzI3NDIxNTQyOQ==&mid=2247484544&idx=1&a ...

  5. Netty 源码解析系列-服务端启动流程解析

    netty源码解析系列 Netty 源码解析系列-服务端启动流程解析 Netty 源码解析系列-客户端连接接入及读I/O解析 五分钟就能看懂pipeline模型 -Netty 源码解析 1.服务端启动 ...

  6. Mybatis3 源码解析系列

    简介 Mybatis作为一个优秀的Java持久化框架,在我们的日常工作中相信都会用到,本次源码解析系列,就开始探索下Mybatis 总结 在MyBatis的学习中,首先通读了<MyBatis3源 ...

  7. openGauss数据库源码解析系列文章——openGauss开发快速入门(二)

    在上一篇openGauss数据库源码解析系列文章--openGauss开发快速入门(上)中,我们介绍了openGauss的安装部署方法,本篇将具体介绍openGauss基本使用. 二. openGau ...

  8. openGauss数据库源码解析系列文章--openGauss简介(一)

    openGauss数据库是华为深度融合在数据库领域多年经验,结合企业级场景要求推出的新一代企业级开源数据库.此前,Gauss松鼠会已经发布了openGauss数据库核心技术系列文章,介绍了openGa ...

  9. SkeyePlayer RTSP/RTMP流媒体超低延迟播放器源码解析系列之H264一帧多NAL写MP4录像花屏问题解决方案

    接上一篇[SkeyePlayer源码解析系列之录像写MP4]之续篇,我们来讲解一下关于H264编码格式中的一帧多nal(Network Abstract Layer, 即网络抽象层),关于H264和N ...

最新文章

  1. Exchange部署之:客户端访问Exchange
  2. numpy中的cov(方差计算)简单介绍
  3. 分布式系统的架构思路
  4. ATS读小文件(内存命中)
  5. 什么甜食可以代表你的家乡?
  6. mysql keepalived双主双活_mysql高可用架构方案之中的一个(keepalived+主主双活)
  7. Veeam 发布 2022 年数据保护趋势报告,开发者需关注哪些点?
  8. 微信小程序:一起玩连线,一个算法来搞定
  9. 第三次冲刺--软件工程
  10. idea无法登录github,显示显示错误信息invalid authentication data的终极解决办法。
  11. WinForm程序启动控制台窗口Console
  12. XWindow与GtkWindow的转换
  13. 解决iTerm中‘zsh-syntax-highlighting‘找不到的问题
  14. 架构篇--系统监控--spring-boot2.0.X 系统原生信息监控,SQL信息监控,cpu温度监控报警,cup磁盘内存使用率监控报警,自定义端点监控以及子节点获取,系统异常邮件通知
  15. Android动画分类与总结
  16. 如何彻底卸载3dmax2020_3dsmax2020卸载/安装失败/如何彻底卸载清除干净3dsmax2020注册表和文件的方法...
  17. sketch如何做设计稿交互_sketch 交互原型制作方法
  18. 铁威马教程之如何轻松同步TNAS和云盘数据
  19. 微信生成二维码报invalid action name hint错误的解决方法
  20. Hadoop相关概念

热门文章

  1. js 加alert后才能执行方法
  2. 通过FactoryBean方式来配置bean
  3. 软工第二次练习——团队展示
  4. text-overflow
  5. textarea选中行删除_如何一键删除表格空行,这个方法才最高级!
  6. Windows核心编程 第四章 进程(中)
  7. 【数字信号处理】傅里叶变换性质 ( 傅里叶变换线性性质 | 傅里叶变换时移性质 )
  8. 【数字信号处理】线性常系数差分方程 ( 概念 | 线性常系数差分方程解法 )
  9. 【MATLAB】进阶绘图 ( colormap 颜色图矩阵分析 | 自定义 colormap 颜色图 | 生成 64 x 3 的 colormap 颜色图矩阵 )
  10. 【嵌入式开发】C语言 命令行参数 函数指针 gdb调试