用JS打造一款記賬App——Redux架構(gòu)設(shè)計(jì)

上一篇我們學(xué)習(xí)了構(gòu)建一個(gè)react項(xiàng)目以及配置webpack并運(yùn)行項(xiàng)目,這篇我們?cè)谝雛edux之前學(xué)習(xí)它的運(yùn)行機(jī)制及設(shè)計(jì)思路。

Redux的架構(gòu)思想

1. state和store 的概念

state是React中定義的應(yīng)用狀態(tài),本質(zhì)是一個(gè)數(shù)據(jù)集的普通對(duì)象,例如

/** 應(yīng)用初始state **/
{
    status: 0,
    todos: []
}

store是應(yīng)用狀態(tài)state的管理者,包含四個(gè)函數(shù):

getState() #獲取整個(gè)state
dispatch(action) #觸發(fā)state改變的唯一途徑
subscribe(listener) #可以理解為DOM中的addEventListener
replayceReducer(nextReducer) #一般在Webpack按需加載的時(shí)候用

二者的關(guān)系 state = store.getState()

Redux規(guī)定:一個(gè)應(yīng)用只應(yīng)有一個(gè)單一的store,其管理著唯一的應(yīng)用狀態(tài)state且不能直接被修改,若要改變state,必須執(zhí)行dispatch一個(gè)action,這是修改應(yīng)用狀態(tài)的唯一方法。

現(xiàn)在您只需要記住action只是一個(gè)包含type屬性的普通對(duì)象即可
例如{type:'INCREMENT'}

我們知道了state是通過(guò)store.getState()獲取的,那么store是怎么來(lái)的呢?那就需要用到了Redux定義的createStore方法了。

import { createStore } from 'redux'
...
//store是將reducer傳入生成的
const store = createStore(reducer, initialState) 

現(xiàn)在您只需要記住reducer是一個(gè)函數(shù),負(fù)責(zé)更新并返回一個(gè)新的state,而initialState主要用于前后端同構(gòu)的數(shù)據(jù)同步(詳情請(qǐng)關(guān)注React服務(wù)端渲染)

2. reducer

這里我們又要用到Redux中定義的另外一個(gè)方法combineReducers。

import { combineReducers } from 'redux'
...
const rootReducer = combineReducers({
  captchaData,
  loginState,
  status,
  userInfo
})

export default rootReducer

具體工作原理是dispatch執(zhí)行action之后,reducer負(fù)責(zé)根據(jù)action的type屬性重新將對(duì)應(yīng)action的payload值賦給nextState

3.Action

上面我們提到了action實(shí)質(zhì)的包含type屬性的普通對(duì)象,這個(gè)type是實(shí)現(xiàn)用戶行為的關(guān)鍵。例如我們?cè)黾右粋€(gè)支出分類

{
    type: 'ADD_CATEGORY',
    payload: {
        id:1,
        content:'交通'
    }
}

當(dāng)然,action可以根據(jù)具體的業(yè)務(wù)來(lái)自由設(shè)定,唯一的必要屬性就是type,甚至我們可以寫(xiě)一個(gè)只包含type屬性的action

下面的action都是合法的

{
    type: 'ADD_CATEGORY',
    id:1,
    content:'交通'
}

{
    tyep: 'ADD_CATEGORY',
    aabbcc: {
        id: 1,
        content: '交通'
    }
}
{
    type: 'ADD_CATEGORY'
}

雖然沒(méi)有約束,但最好還是遵循規(guī)范

這里有一個(gè)思考問(wèn)題,action難道就只是包含type等一些屬性的對(duì)象嗎?這里留一個(gè)懸念,后面我在具體用Redux的過(guò)程中再講。

Redux優(yōu)缺點(diǎn)

Action Creator => action => store.dispatch(action) => reducer(state, action) => 原state state = nextState

優(yōu)點(diǎn):清晰的數(shù)據(jù)流向,讓我們處理復(fù)雜的業(yè)務(wù)邏輯,可以更加方便的梳理業(yè)務(wù)線,action只負(fù)責(zé)執(zhí)行邏輯操作和數(shù)據(jù)獲取,reducer只負(fù)責(zé)返回?cái)?shù)據(jù)集,然后connect給react中的組件使用,讓react只關(guān)心交互界面的邏輯,無(wú)需關(guān)心數(shù)據(jù)邏輯。
另外我們使用redux還有如下好處

  • 方便地能夠?qū)?yīng)用狀態(tài)存儲(chǔ)到本地并且重啟動(dòng)時(shí)能夠讀取恢復(fù)狀態(tài)
  • 方便地能夠在服務(wù)端完成初始狀態(tài)設(shè)置,并且完成狀態(tài)的服務(wù)端渲染
  • 能夠序列化記錄用戶操作,能夠設(shè)置狀態(tài)快照,從而方便進(jìn)行Bug報(bào)告與開(kāi)發(fā)者的錯(cuò)誤重現(xiàn)
  • 能夠?qū)⒂脩舻牟僮骰蛘呤录鬟f給其他環(huán)境而不需要修改現(xiàn)有代碼
  • 能夠添加重放或者撤銷功能而不需要重構(gòu)代碼
  • 能夠在開(kāi)發(fā)過(guò)程中實(shí)現(xiàn)狀態(tài)歷史的回溯,或者根據(jù)Action的歷史重現(xiàn)狀態(tài)
  • 能夠?yàn)殚_(kāi)發(fā)者提供全面透徹的審視和修改現(xiàn)有開(kāi)發(fā)工具的接口,從而保證產(chǎn)品的開(kāi)發(fā)者能夠根據(jù)他們自己的應(yīng)用需求打造專門的工具
  • 能夠在復(fù)用現(xiàn)在大部分業(yè)務(wù)邏輯的基礎(chǔ)上構(gòu)造不同的界面

使用Redux我們必須遵循Redux的守則

  • 必須使用基本對(duì)象與數(shù)組來(lái)描述應(yīng)用狀態(tài)
  • 必須使用基本的對(duì)象來(lái)描述系統(tǒng)變化
  • 必須使用純函數(shù)來(lái)處理系統(tǒng)中的業(yè)務(wù)邏輯

缺點(diǎn):由于必須遵守Redux的守則,即使很簡(jiǎn)單的業(yè)務(wù)邏輯,我們也必須有store,reducer,action.type。這樣給人的感覺(jué)就是殺雞用牛刀,所以對(duì)于簡(jiǎn)單的業(yè)務(wù)邏輯,我們無(wú)需引用Redux庫(kù)。

最后,Redux真正的靈魂在于其設(shè)計(jì)思想,很多時(shí)候并不一定需要引用Redux庫(kù)本身,但可以嘗試使用redux的思想。

import React, { Component } from 'react'

class Counter extends Component {
  state = { value: 0 }
  
  increment = () => {
    this.setState(prevState => ({
      value: prevState.value + 1
    }))
  }

  decrement = () => {
    this.setState(prevState => ({
      value: prevState.value - 1
    }))
  }
  
  render() {
    return (
      <div>
        {this.state.value}
        <button onClick={this.increment}>+</button>
        <button onClick={this.decrement}>-</button>
      </div>
    )
  }

使用redux,我們可能會(huì)有禁用Local State的習(xí)慣,其實(shí)Local State有時(shí)候恰恰是用最簡(jiǎn)單的方法幫我們解決問(wèn)題。我們對(duì)上面代碼用redux思想進(jìn)行改造

import React, { Component } from 'react'

const counter = (state = { value:0 }, action) => {
    switch (action.type) {
        case 'INCREMENT':
            return { value: state.value + 1}
        case 'DECREMENT':
            return { value: state.value - 1}
        default:
            return state
    }
}

class Counter extends Component {
    state = counter(undefined, {})
    
    dispatch(action) {
        this.setState(prevState => counter(prevState, action))
    }
    
    increment = () => {
        this.dispatch({type: 'INCREMENT'})
    }
    
    decrement = () => {
        this.dispatch({type: 'DECREMENT'})
    }
    
    render() {
      return (
         <div>
         {this.state.value}
            <button onClick={this.increment}>+</button>
            <button onClick={this.decrement}>-</button>
         </div>
    )
  }
}

總結(jié)

  • store由Redux的 createStore(reducer) 生成
  • state通過(guò)store.getState()獲取,本質(zhì)是一個(gè)存儲(chǔ)整個(gè)應(yīng)用狀態(tài)數(shù)據(jù)集的對(duì)象
  • reducer用于更新state并返回nextState的函數(shù),且reducer必須有返回值,否則nextState將為undefined
  • action是一個(gè)包含type屬性的普通對(duì)象,用于dipatch來(lái)改變state

這一篇我們理解了Redux的幾個(gè)概念以及設(shè)計(jì)思想,下一篇我們要在項(xiàng)目中引用Redux庫(kù)并學(xué)習(xí)怎么使用它。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容