博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Redux 源码解析系列(一) -- Redux的实现思想
阅读量:7251 次
发布时间:2019-06-29

本文共 4169 字,大约阅读时间需要 13 分钟。

文章来源: 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.text      break    case 'UPDATE_TITLE_COLOR':      appState.title.color = action.color      break    default:      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) => {    // 覆盖原先的appState    appState = 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 = null  const listeners = []  const subscribe = (listener) => listeners.push(listener)  const getState = () => state  const dispatch = (action) => {    // 可以看到 由于reducer返回的是一个新的object,那在外层,我们就可以对比nextProps跟t his.props 来决定是否渲染    state = reducer(state, action)    listeners.forEach((listener) => listener())  }  dispatch({}) // 初始化 state  return { getState, dispatch, subscribe }}

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

  • getState

  • dispatch

  • subscribe

  • 初始reducer的状态

四个步骤

// 定一个 reducer, 负责管理数据变化还有初始化appState的数据function reducer (state, action) {  /* 初始化 state 和 switch case */}// 生成 storeconst 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的大致思想。

参考文档:

转载地址:http://hmhbm.baihongyu.com/

你可能感兴趣的文章
一个问题看系统数据库设计
查看>>
镜像仓库Harbor私服高可用策略分析及部署
查看>>
重写cnodejs学习整理
查看>>
从浏览器渲染的角度谈谈html标签的语义化
查看>>
文件权限及特殊权限管理SUID、SGID和Sticky
查看>>
iis 7 asp.net ajax post 请求字节过大报错问题解决办法
查看>>
高仿腾讯QQ即时通讯IM项目
查看>>
winform 中xml简单的创建和读取
查看>>
活动设计的“七宗罪”(转)
查看>>
如何在ChemDraw中输入℃温度符号
查看>>
SSH-Struts第二弹:一个Form提交两个Action
查看>>
词法分析
查看>>
Linux命令(二)
查看>>
Web登录验证之 Shiro
查看>>
LeeCode-Sort Colors
查看>>
Snort2.9.2.3 Installation on CentOS 6.2
查看>>
我的友情链接
查看>>
给软件工程师的自学建议
查看>>
Linux下SVN的备份方式
查看>>
hadoop 3.0.0 alpha1 分布式搭建
查看>>