介紹 Redux
Redux是一個(gè)數(shù)據(jù)狀態(tài)管理插件如输,搭配 React 特別合適利耍,詳細(xì)的用法可見(jiàn)Redux官網(wǎng)
使用場(chǎng)景
無(wú)論是移動(dòng)端還是 pc 端量没,當(dāng)你使用 React 或者 vue 開(kāi)發(fā)組件化的 SPA 程序時(shí)扯再,組件之間共享信息是一個(gè)非常大的問(wèn)題荚板。例如,用戶(hù)登錄之后客戶(hù)端會(huì)存儲(chǔ)用戶(hù)信息(如userid
队寇、頭像等)膘掰,而系統(tǒng)的很多個(gè)組件都會(huì)用到這些信息,例如收藏佳遣、點(diǎn)贊识埋、評(píng)論等。這些組件在用到用戶(hù)信息時(shí)零渐,難道每次使用都重新獲取一遍窒舟?———— 自然不是這樣。因此每個(gè)系統(tǒng)都需要一個(gè)管理多組件使用的公共信息的功能诵盼,這就是 Redux 的作用惠豺。同理,vue 也有相應(yīng)的工具拦耐,即 vuex 耕腾,可以自己去 github 上搜索相關(guān)資料。
初學(xué)者可能通過(guò)這幾句話無(wú)法真實(shí)理解它的用意杀糯,此時(shí)你只需要記咨ò场:只要使用 React 開(kāi)發(fā)系統(tǒng),你絕大部分都需要結(jié)合 Redux 來(lái)使用固翰,后面的課程我們?cè)敿?xì)講解 Redux 在實(shí)際項(xiàng)目中的使用狼纬,課程結(jié)束后,你就會(huì)明白其中的道理骂际。
安裝
如果單純使用 Redux 僅僅安裝 Redux 即可疗琉,執(zhí)行npm install redux --save
,不過(guò)在 React 中使用 Redux 肯定會(huì)用到 react-redux
這一工具歉铝,因此這里一起安裝完盈简,執(zhí)行npm install react-redux --save
。
基本使用
可以參見(jiàn)./app/redux-demo.js
中的例子,如下代碼
// 定義計(jì)算規(guī)則柠贤,即 reducer
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
// 根據(jù)計(jì)算規(guī)則生成 store
let store = createStore(counter)
// 定義數(shù)據(jù)(即 state)變化之后的派發(fā)規(guī)則
store.subscribe(() => {
console.log('current state', store.getState())
})
// 觸發(fā)數(shù)據(jù)變化
store.dispatch({type: 'INCREMENT'})
store.dispatch({type: 'INCREMENT'})
store.dispatch({type: 'DECREMENT'})
簡(jiǎn)單幾十行代碼香浩,就詮釋了 Redux 的設(shè)計(jì)理念,這里簡(jiǎn)單分析一下:
- Redux 是一個(gè)管理數(shù)據(jù)的工具臼勉,我們創(chuàng)建一個(gè)
store
變量用來(lái)管理數(shù)據(jù)邻吭。而這個(gè)store
不是憑空創(chuàng)建的,創(chuàng)建它的前提是宴霸,得設(shè)定一個(gè)管理規(guī)則囱晴。以上代碼中,我們的管理規(guī)則是:數(shù)據(jù)(即state
)默認(rèn)是 0瓢谢,傳入INCREMENT
就加一畸写,傳入DECREMENT
就減一 - 創(chuàng)建
store
用來(lái)管理數(shù)據(jù),具體的管理形式是什么呢恩闻?第一艺糜,要通過(guò)一個(gè)函數(shù)來(lái)觸發(fā)數(shù)據(jù)的變化,即dispatch
幢尚,觸發(fā)的時(shí)候一定要符合之前定制的規(guī)則,否則無(wú)效翅楼。第二尉剩,數(shù)據(jù)一旦發(fā)生變化時(shí),會(huì)導(dǎo)致怎樣后果毅臊,即subscribe
中定義的函數(shù)會(huì)執(zhí)行理茎。第三,如何取得當(dāng)前的數(shù)據(jù)管嬉,即store.getState()
皂林。這一塊,熟悉設(shè)計(jì)模式的同學(xué)不難理解蚯撩,這就是普通的發(fā)布和訂閱的設(shè)計(jì)模式础倍,也是js種慣用的設(shè)計(jì)模式。 - 還有一點(diǎn)特別要注意胎挎,即在規(guī)則函數(shù)中沟启,數(shù)據(jù)變化時(shí)要
return
一個(gè)新的值,而不是直接修改原來(lái)的值犹菇。這一點(diǎn)和之前提到的Immutable.js
一樣德迹,都是使用了不可變數(shù)據(jù)這一概念。這種設(shè)計(jì)方式明確了數(shù)據(jù)的變化時(shí)段揭芍,使得數(shù)據(jù)管理更清晰胳搞,復(fù)雜度更低。
------------------ 分割線 ------------------
Redux 和 React 集成
這塊一開(kāi)始介紹可能會(huì)感覺(jué)有點(diǎn)混亂,要做好心理準(zhǔn)備肌毅。
創(chuàng)建 store
跟上次講過(guò)得簡(jiǎn)單 demo 一樣币厕,首先也需要?jiǎng)?chuàng)建一個(gè)store
,參見(jiàn)./app/store/configureStore.js
的代碼芽腾。之前的 demo 提到旦装,創(chuàng)建store
之前要有規(guī)則,這里的第一個(gè)參數(shù)就是這個(gè)規(guī)則摊滔,后面會(huì)詳細(xì)講到阴绢。
const store = createStore(rootReducer, initialState,
// 觸發(fā) redux-devtools
window.devToolsExtension ? window.devToolsExtension() : undefined
)
第二個(gè)參數(shù)即初始化的數(shù)據(jù),第三個(gè)參數(shù)可調(diào)起 chrome 擴(kuò)展程序艰躺,具體可參見(jiàn) redux-devtools
創(chuàng)建規(guī)則(Reducer)
使用 Redux 時(shí)呻袭,剛才提到的“規(guī)則”被稱(chēng)作reducer
(就是一個(gè)統(tǒng)一的稱(chēng)呼,不比去糾結(jié))腺兴,因此這里的數(shù)據(jù)規(guī)則代碼都在./app/reducers
目錄下左电。
先看userinfo.js
的代碼,跟上次 demo 中的幾乎一樣页响,唯一的區(qū)別就是將 const 都寫(xiě)到了./app/constants/userinfo.js
中篓足。之所以這樣做,是因?yàn)檫@些 const 會(huì)在多個(gè)文件中使用闰蚕,因此要抽象出來(lái)栈拖。
再看index.js
的代碼,它用combineReducers
這個(gè)函數(shù)對(duì)userinfo.js
的數(shù)據(jù)進(jìn)行了封裝没陡,這樣做是為了更好的擴(kuò)展性涩哟。試想,一個(gè)系統(tǒng)中存儲(chǔ)在 Redux 中的數(shù)據(jù)可能會(huì)有很多盼玄,我們這里已經(jīng)有一個(gè)userinfo.js
處理用戶(hù)數(shù)據(jù)贴彼,和可能哪天就再加nav.js
處理導(dǎo)航數(shù)據(jù)、加ad.js
處理廣告數(shù)據(jù)……
上次的demo中埃儿,state
就是一個(gè)數(shù)據(jù)器仗,可以進(jìn)行state + 1
或state - 1
,數(shù)據(jù)結(jié)構(gòu)非常簡(jiǎn)單蝌箍。而現(xiàn)在青灼,數(shù)據(jù)結(jié)構(gòu)復(fù)雜太多,必須分組管理妓盲。因此我們需要用state.userinfo
來(lái)表示用戶(hù)數(shù)據(jù)杂拨,state.nav
表示導(dǎo)航數(shù)據(jù),state.ad
表示廣告數(shù)據(jù)…… ———— 這就是用combineReducers
分裝各個(gè) reducer 的作用悯衬。
創(chuàng)建 action
上次的 demo 中弹沽,最后執(zhí)行數(shù)據(jù)變化時(shí)store.dispatch({type: 'INCREMENT'})
檀夹,這里的{type: 'INCREMENT'}
是我們手動(dòng)寫(xiě)上的,而在實(shí)際的應(yīng)用中策橘,我們需要用一些函數(shù)將它分裝起來(lái)炸渡,即./app/actions
中的文件,雖然此處只有userinfo.js
這一個(gè)文件丽已。
在userinfo.js
中蚌堵,我們把每個(gè)業(yè)務(wù)操作都分裝為一個(gè)函數(shù),該函數(shù)接收data
沛婴,然后再根據(jù) reducer 的規(guī)則對(duì) data 進(jìn)行分裝吼畏,最后返回。當(dāng)然嘁灯,最后返回的結(jié)果肯定還是會(huì)交給dispatch
來(lái)處理泻蚊,這是后面要說(shuō)的。
結(jié)合到 React
先看./app/index.js
丑婿,重點(diǎn)注意下面這些代碼性雄。這里,創(chuàng)建了store
并傳遞給<Provider>
組件羹奉,然后讓<Provider>
組件作為所有組件的根節(jié)點(diǎn)秒旋。
import { Provider } from 'react-redux'
import configureStore from './store/configureStore'
const store = configureStore()
render(
<Provider store={store}>
<Hello/>
</Provider>,
document.getElementById('root')
)
然后看./containers/Hello.jsx
,注意下面這些代碼尘奏。通過(guò)下面的封裝滩褥,就把userinfo
和userinfoActions
當(dāng)做props
傳入到Hello
中了,即在Hello
組件中通過(guò)this.props.userinfo
和this.props.userinfoActions
即可獲取數(shù)據(jù)和 actions
function mapStateToProps(state) {
return {
userinfo: state.userinfo
}
}
function mapDispatchToProps(dispatch) {
return {
userinfoActions: bindActionCreators(userinfoActions, dispatch)
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Hello)
獲取了數(shù)據(jù)和 actions 該怎么用呢炫加?我們將它們傳遞給子組件,A
和B
組件負(fù)責(zé)展示數(shù)據(jù)铺然,C
組件負(fù)責(zé)觸發(fā)actions俗孝。具體可參見(jiàn)各個(gè)組件的源代碼。
render() {
return (
<div>
<p>hello world</p>
<hr/>
<A userinfo={this.props.userinfo}/>
<hr/>
<B userinfo={this.props.userinfo}/>
<hr/>
<C actions={this.props.userinfoActions}/>
</div>
)
}
運(yùn)行代碼之后魄健,就會(huì)看到數(shù)據(jù)變化的效果了赋铝。
總結(jié):接下來(lái)的實(shí)際項(xiàng)目中,也會(huì)像上面這樣使用 Redux沽瘦,如果這里還有點(diǎn)不明革骨,接下來(lái)的課程也會(huì)再詳細(xì)講解————當(dāng)然,還是要盡早搞明白好