快速理解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

本文若是有錯,歡迎指出

相關文章
相關標籤/搜索