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

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

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

  • createStoreweb

  • combineReducersredux

  • bindActionCreators性能优化

  • applyMiddlewareapp

  • compose框架

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

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 */
}

// 生成 store
const store = createStore(reducer)

// 监听数据变化从新渲染页面
store.subscribe(() => renderApp(store.getState()))

// 首次渲染页面
renderApp(store.getState()) 

// 后面能够随意 dispatch 了,页面自动更新
store.dispatch(...)

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

一、共享状态 -> dispatch

二、store统一管理 dispatch getState

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

四、最终初始化整个reducer

以上就是redux的大体思想。

参考文档:

http://huziketang.com/books/r...

相关文章
相关标签/搜索