快速理解redux

redux 只是一个状态管理

简述

本篇文章主要说明redux的基本原理以及如何使用javascript

阅读前需了解(本文中仅做简单描述,详细了解请自行Google)


  • 纯函数
    符合如下两点性质的函数即为纯函数vue

    • 函数执行不改变外部变量
    • 函数的输出结果仅依赖于输入参数
  • 高阶组件java

    • 即一个函数,接收一个组件做为参数,输出一个新组件
  • 观察者模式react

    • 即发布订阅模式,能够理解为给一个事件绑定多个函数,事件触发时多个绑定函数所有执行
  • react基础git

  • wepy基础(有vue基础也能够)github

redux

react中的context属性

简单的说,context就是一个全局变量,它能够被一个高阶组件及该高阶组件的全部子组件,孙组建等等共享redux

举个例子,下图是一个react页面

  • react页面树形结构react页面树形结构
  • 正常的状态提高及数据下放
    react的props状态传递
  • 使用context的树形结构
    context

由上面三图能够看出,本应一层一层传递的数据,在使用context后,变得方便了。
高阶组件如下的全部子组件均可以直接从context中获取数据。app

context并不完美

这看似方便的方法,实际上引起了一个老生常谈的问题,即全局变量控制问题
context 里面的数据能被随意接触就能被随意修改,每一个组件都可以改 context 里面的内容会致使程序的运行不可预料
同时context的出现也打破了组件和组件之间经过 props 传递数据的规范,极大地加强了组件之间的耦合性
试想,如果全部组件均可以经过xxx='xxx'来修改状态,咱们获取并控制当前状态的难度是否变大?
在大型复杂项目中,咱们可能都没法肯定某数据是如何变成当前值的
为了不这种状况出现,redux就出现了函数

redux解决了问题

为了解决模块(组件)之间须要共享数据数据可能被任意修改致使不可预料的结果时间的矛盾,
redux团队想出了一个办法,即把事情搞复杂一些,提升数据修改的门槛:模块(组件)之间能够共享数据,也能够改数据。可是咱们约定,这个数据并不能直接改,你只能执行某些我容许的某些修改,并且你修改的必须大张旗鼓地告诉我。gitlab

因此,修改数据的函数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
  }
}

全部的数据都必须经过调用dispatch修改

dispatch({ type: 'UPDATE_TITLE_TEXT', text: 'hello world' }) // 修改标题文本
dispatch({ type: 'UPDATE_TITLE_COLOR', color: 'blue' }) // 修改标题颜色

如图所示

  • 引入redux前,各组件直接修改数据

图片描述

  • 如今,必须经过dispatch修改数据

图片描述

抽离store并监控数据变化

咱们把它们集中到一个地方,给这个地方起个名字叫作 store,而后构建一个函数 createStore,用来专门生产这种 state 和 dispatch 的集合,这样别的 App 也能够用这种模式了:

/**
*@param
*state 初始状态
*stateChanger 一个修改state的函数
*/
function createStore (state, stateChanger) {
  const getState = () => state
  const dispatch = (action) => stateChanger(state, action)
  return { getState, dispatch }
}

本例中页面经过renderApp等函数刷新,为了不dispatch后页面数据不变化(render函数不执行)
咱们必须引入观察者模式,使dispatch后,app自动执行render函数

function createStore (state, stateChanger) {
  const listeners = []
  const subscribe = (listener) => listeners.push(listener)
  const getState = () => state
  const dispatch = (action) => {
    stateChanger(state, action)
    listeners.forEach((listener) => listener())
  }
  return { getState, dispatch, subscribe }
}

function renderApp (appState) {
  renderTitle(appState.title)
  renderContent(appState.content)
}

function renderTitle (title) {
  const titleDOM = document.getElementById('title')
  titleDOM.innerHTML = title.text
  titleDOM.style.color = title.color
}

function renderContent (content) {
  const contentDOM = document.getElementById('content')
  contentDOM.innerHTML = content.text
  contentDOM.style.color = content.color
}

let appState = {
  title: {
    text: 'React.js 小书',
    color: 'red',
  },
  content: {
    text: 'React.js 小书内容',
    color: 'blue'
  }
}

function stateChanger (state, action) {
  switch (action.type) {
    case 'UPDATE_TITLE_TEXT':
      state.title.text = action.text
      break
    case 'UPDATE_TITLE_COLOR':
      state.title.color = action.color
      break
    default:
      break
  }
}

const store = createStore(appState, stateChanger)
store.subscribe(() => renderApp(store.getState())) // 监听数据变化

renderApp(store.getState()) // 首次渲染页面
store.dispatch({ type: 'UPDATE_TITLE_TEXT', text: '《React.js 小书》' }) // 修改标题文本
store.dispatch({ type: 'UPDATE_TITLE_COLOR', color: 'blue' }) // 修改标题颜色

至此,咱们已经大概构建了一个redux的骨架,接下来咱们将完善它

严重的性能问题

不知道读者有没有发现,当咱们经过dispatch修改标题的文字时,整个App就会刷新一次,
当咱们修改文本的颜色时,整个App也会刷新一次,这样就频繁的所有刷新就形成了极大的性能问题
那么,可否修改title,仅刷新title;修改content,也仅刷新content呢?
咱们使render函数接收2个参数(newState, oldState = {})而且在刷新前进行比较

function renderApp (newAppState, oldAppState = {}) { // 防止 oldAppState 没有传入,因此加了默认参数 oldAppState = {}
  if (newAppState === oldAppState) return // 数据没有变化就不渲染了
  console.log('render app...')
  renderTitle(newAppState.title, oldAppState.title)
  renderContent(newAppState.content, oldAppState.content)
}

function renderTitle (newTitle, oldTitle = {}) {
  if (newTitle === oldTitle) return // 数据没有变化就不渲染了
  console.log('render title...')
  const titleDOM = document.getElementById('title')
  titleDOM.innerHTML = newTitle.text
  titleDOM.style.color = newTitle.color
}

function renderContent (newContent, oldContent = {}) {
  if (newContent === oldContent) return // 数据没有变化就不渲染了
  console.log('render content...')
  const contentDOM = document.getElementById('content')
  contentDOM.innerHTML = newContent.text
  contentDOM.style.color = newContent.color
}

这样就能够提升性能了吧,每次只刷新须要刷新部分啦~~


才怪!
咱们确实修改了对象内的属性值,可是newState和oldState所指的不仍是一个对象吗?
因此为了进行判断,咱们还要修改前面的stateChanger函数

function stateChanger (state, action) {
  switch (action.type) {
    case 'UPDATE_TITLE_TEXT':
      return { // 构建新的对象而且返回
        ...state,
        title: {
          ...state.title,
          text: action.text
        }
      }
    case 'UPDATE_TITLE_COLOR':
      return { // 构建新的对象而且返回
        ...state,
        title: {
          ...state.title,
          color: action.color
        }
      }
    default:
      return state // 没有修改,返回原来的对象
  }
}

如今咱们才真正提升了性能

reduer

为了让程序的结构更加清晰,咱们把原始state放入stateChanger中,并把stateChanger更名为reducer

  • 为何叫redcer? 别问为何,没有理由!
function reducer (state, action) {
  if (!state) {
    return {
      title: {
        text: 'hello world',
        color: 'red',
      },
      content: {
        text: 'hello world content',
        color: 'blue'
      }
    }
  }
  switch (action.type) {
    case 'UPDATE_TITLE_TEXT':
      return {
        ...state,
        title: {
          ...state.title,
          text: action.text
        }
      }
    case 'UPDATE_TITLE_COLOR':
      return {
        ...state,
        title: {
          ...state.title,
          color: action.color
        }
      }
    default:
      return state
  }
}

总结


如今的代码和react,wepy关系都不大,在下一篇文章中,我会讲述如何具体地在react中使用redux
感谢 @胡子大哈 老师的《react小书》,本章有不少代码都是摘自该书

本文参考

react小书

所有代码

make-redux

本文若是有错,欢迎指出

相关文章
相关标签/搜索