redux详解

简单代码

const appState = {
  title: {
    text: '这是title',
    color: 'red',
  },
  content: {
    text: '这是content',
    color: 'blue'
  }
}
<div id='title'></div>
<div id='content'></div>
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
}
renderApp(appState)

事情有这么简单?

loadDataFromServer()
doSomething()
doSomthingMore()
doSomthingBad()
// ...
renderApp(appState)

数据的不可预测性

矛盾

模块(组件)之间需要共享数据数据

可能被任意修改导致不可预料的结果

限制数据修改

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({ type: 'UPDATE_TITLE_TEXT', text: '修改title' })
dispatch({ type: 'UPDATE_TITLE_COLOR', color: 'blue' })
renderApp(appState) 

所有对数据的修改必须进过dispatch

抽出store

function createStore (state, stateChanger) {
  const getState = () => state
  const dispatch = (action) => stateChanger(state, action)
  return { getState, dispatch }
}
const store = createStore(appState, stateChanger)

renderApp(store.getState())
store.dispatch({ type: 'UPDATE_TITLE_TEXT', text: '修改title' })
store.dispatch({ type: 'UPDATE_TITLE_COLOR', color: 'blue' })
renderApp(store.getState())

每次都需要手动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 }
}
const store = createStore(appState, stateChanger)
store.subscribe(() => renderApp(store.getState()))

renderApp(store.getState())
store.dispatch({ type: 'UPDATE_TITLE_TEXT', text: '修改title' })
store.dispatch({ type: 'UPDATE_TITLE_COLOR', color: 'blue' }) 

纯函数

一个函数的返回结果只依赖于它的参数,并且在执行过程里面没有副作用,我们就把这个函数叫做纯函数

const a = 1
const foo = (b) => a + b
foo(2)
const a = 1
const foo = (x, b) => x + b
foo(1, 2)
const foo = (obj, b) => {
  obj.x = 2
  return obj.x + b
}
const counter = { x: 1 }
foo(counter, 2)
counter.x
const foo = (b) => {
  const obj = { x: 1 }
  obj.x = 2
  return obj.x + b
}

页面性能

function renderApp (appState) {
  console.log('render app...')
  renderTitle(appState.title)
  renderContent(appState.content)
}
function renderTitle (title) {
  console.log('render title...')
  const titleDOM = document.getElementById('title')
  titleDOM.innerHTML = title.text
  titleDOM.style.color = title.color
}

function renderContent (content) {
  console.log('render content...')
  const contentDOM = document.getElementById('content')
  contentDOM.innerHTML = content.text
  contentDOM.style.color = content.color
}
const store = createStore(appState, stateChanger)
store.subscribe(() => renderApp(store.getState()))

renderApp(store.getState()) // 首次渲染页面
store.dispatch({ type: 'UPDATE_TITLE_TEXT', text: '修改title' })
store.dispatch({ type: 'UPDATE_TITLE_COLOR', color: 'blue' })

简单点 加个判断

function renderApp (newAppState, oldAppState = {}) {
  if (newAppState === oldAppState) return // 数据没有变化就不渲染了
}

function renderTitle (newTitle, oldTitle = {}) {
  if (newTitle === oldTitle) return // 数据没有变化就不渲染了
}

function renderContent (newContent, oldContent = {}) {
  if (newContent === oldContent) return // 数据没有变化就不渲染了
}

const store = createStore(appState, stateChanger)
let oldState = store.getState() 
store.subscribe(() => {
  const newState = store.getState() 
  renderApp(newState, oldState) 
  oldState = newState
})

达到效果了么?

对象拷贝

浅拷贝(创建新对象的时候只需要对发生变化的属性进行变更)

let appState = {
  title: {
    text: '这是title',
    color: 'red',
  },
  content: {
    text: '这是content',
    color: 'blue'
  }
}
let newAppState = Object.assign({}, appState);
newAppState.title = Object.assign({}, newAppState.title, {text: '修改title'})

优化后的代码

function stateChanger (state, action) {
  let newAppState = {}
  switch (action.type) {
    case 'UPDATE_TITLE_TEXT':
      newAppState = Object.assign({}, state)
      newAppState.title = Object.assign({}, newAppState.title, {text: action.text})
      return newAppState
    case 'UPDATE_TITLE_COLOR':
      newAppState = Object.assign({}, state)
      newAppState.title = Object.assign({}, newAppState.title, {color: action.color})
      return newAppState
    default:
      return state
  }
}
function createStore (state, stateChanger) {
  const listeners = []
  const subscribe = (listener) => listeners.push(listener)
  const getState = () => state
  const dispatch = (action) => {
    state = stateChanger(state, action)
    listeners.forEach((listener) => listener())
  }
  return { getState, dispatch, subscribe }
}
const store = createStore(appState, stateChanger)
let oldState = store.getState()
store.subscribe(() => {
  const newState = store.getState() 
  renderApp(newState, oldState) 
  oldState = newState 
})

代码再次简化

合并appState和stateChanger

function stateChanger (state, action) {
  if (!state) {
    return {
      title: {
        text: '这是title',
        color: 'red',
      },
      content: {
        text: '这是content',
        color: 'blue'
      }
    }
  }
  let newAppState = {}
  switch (action.type) {
    case 'UPDATE_TITLE_TEXT':
      newAppState = Object.assign({}, state)
      newAppState.title = Object.assign({}, newAppState.title, {text: action.text})
      return newAppState
    case 'UPDATE_TITLE_COLOR':
      newAppState = Object.assign({}, state)
      newAppState.title = Object.assign({}, newAppState.title, {color: action.color})
      return newAppState
    default:
      return state
  }
}

代码再次简化

改写createStore

function createStore (reducer) {
  let state = null
  const listeners = []
  const subscribe = (listener) => listeners.push(listener)
  const getState = () => state
  const dispatch = (action) => {
    state = reducer(state, action)
    listeners.forEach((listener) => listener())
  }
  dispatch({}) // 初始化 state
  return { getState, dispatch, subscribe }
}

const store = createStore(stateChanger)

悄悄换了参数名

再仔细看下stateChanger这个函数

大功告成

简单的数据依赖demo

数据可随意修改

引入dispatch,修改数据必须使用action

引入createStore,用于产生store,包含了getStore和fispatch

通过store.subscribe ,订阅数据修改,执行事件

由于性能问题,引入对象拷贝(共享结构的对象)

优化stateChanger为纯函数,用于初始state,以及根据action提供新的action

用法

// 定一个 reducer
function reducer (state, action) {
  /* 初始化 state 和 switch case */
}

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

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

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

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

谢谢

deck

By Joson Chen

deck

  • 308