如今咱們就從Redux是什麼,Redux的三大原則和Redux的核心API開始介紹Redux,並說明Redux如何與React結合使用,以及它在Flux基礎上的改變。前端
咱們都知道Flux它自己既不是庫,也不是框架,而是一種應用的架構思想。而Redux呢,它的核心代碼能夠理解成一個庫,但同時也強調和Flux相似的架構思想。
從設計上看,Redux參考了Flux的設計,可是對Flux許多冗餘的部分作了簡化,同時將函數式編程的思想融合其中。
很是有意思的是,Redux是從一個實驗開始的,做者並無想到Redux會變得如此重要又被普遍使用,他只是爲了經過Flux思想解決他的熱重載及時間旅行的問題而已。
Redux自己很是簡單,它的設計思想與React有殊途同歸之妙,均是但願用最少的API實現最核心的功能。Redux自己只把本身定位成一個可預測的狀態容器。
「Redux」自己指redux這個npm包,它提供若干API讓咱們使用reducer建立store,並可以更新store中的數據或獲取store中最新的狀態。而「Redux應用」則是指使用了redux這個npm包並結合了視圖層實現(如React)及其餘前端應用必備組件(路由庫,請求庫等)組成的完整的類Flux思想的前端應用。react
想要理解Redux,必需要知道Redux設計和使用的三大原則。npm
單一數據源
在傳統的MVC框架中,咱們能夠根據須要建立無數個Model,而Model之間能夠互相監聽、觸發事件甚至循環或嵌套觸發事件,這些在Redux中都是不容許的。
由於在Redux的思想裏,一個應用永遠只有惟一的數據源。咱們的第一反應多是:若是一個複雜應用,強制要求惟一的數據源豈不是會產生一個特別龐大的JavaScript對象。
實際上,使用單一的數據源的好處在於整個應用狀態保存在一個對象中,這樣咱們隨時能夠提取出整個應用的狀態進行持久化。此外,這樣的設計也爲服務端渲染提供了可能。
至於咱們擔憂的數據源對象過於龐大的問題,咱們能夠經過combineReducers化解。編程
狀態是隻讀的
在Redux中,咱們並不會本身用代碼來定義一個store。取而代之的是,咱們定義一個reducer,它的功能是根據當前觸發的action對當前應用的狀態進行迭代,這裏咱們並無直接修改應用的狀態,而是返回了一份全新的狀態。
Redux提供的createStore方法會根據reducer生成store。最後,咱們能夠利用store.dispatch方法來達到修改狀態的目的。redux
狀態修改均由純函數完成
在Redux裏,咱們經過定義reducer來確認狀態的修改,而每個reducer都是純函數,這意味着它沒有反作用,即接受必定的輸入,一定會獲得必定的輸出。
這樣設計的好處不只在於reducer裏對狀態的修改變得簡單、純粹、可測試,更有意思的是,Redux利用每次新返回的狀態生成酷炫的時間旅行調試方式,讓跟蹤每一次由於觸發action而改變狀態的結果成爲了可能。後端
Redux的核心是一個store,這個store由Redux提供的createStore方法生成。要想生成store,必須傳入reducers。
在Redux裏,負責響應action並修改數據的角色是reducer。reducer本質上是一個純函數,它有兩個參數state和action。reducer的職責就是根據state和action計算出新的state。
在實際應用中,reducer在處理state時,還須要有一個特殊的非空判斷。很顯然,reducer第一次執行的時候,並無任何的state,而reducer的最終職責是返回新的state,所以須要在這種特殊狀況下返回一個定義好的state。這也就是咱們會定義一個defaultState的緣由。
下面來介紹一下Redux中最核心的API--createStore:bash
import { createStore } from 'redux';
const store = createStore(reducers);
複製代碼
經過createStore方法建立的store是一個對象,它自己包含4個方法。架構
在當前的項目中,模塊化開發已經成爲主流。我的認爲在模塊化開發中,最重要的即是目錄結構的設計。框架
在這裏咱們對目錄進行了比較細緻的劃分:ide
我的認爲在Redux項目中這樣的目錄設計是比較符合模塊化設計思想的目錄。(仁者見仁智者見智)
接下來我就介紹一下Redux具體的步驟和流程。(已請求簡單列表數據爲例)
store中的數據是由reducer決定的,因此第一步先完成reducer。
const defaultState = {
homeList: []
}
export default function(state = defaultState, action) {
switch(action.type) {
case GET_HOME_LIST:
return {
homeList: action.homeList
};
default:
return defaultState
}
}
複製代碼
export const getHomeList = {
type: GET_HOME_LIST,
// 模擬從後端接收到的假數據
homeList: [0,1,2,3]
}
複製代碼
一個應用的常量應該是惟一的,把常量放在一個文件夾裏同時按照模塊劃分,有利於防止變量名的衝突。
// home
export const GET_HOME_LIST = 'GET_HOME_LIST';
複製代碼
該文件是用來建立store的。
import { createStore,
combineReducers} from 'redux'
import HomeReducer from './reducers/home/index';
// 多個 reducer 合併成一個
const rootReducer = combineReducers({
home: HomeReducer
})
// 建立 store 只能接收到 一個 reducer
// 因此 建立以前 合併一下
export default createStore(rootReducer)
複製代碼
class App extends ImmutableComponent {
render() {
return (
// store成爲全局變量,任何組件均可以訪問了
<Provider store={store}>
<BrowserRouter>
<Header />
<Route path="/" component={Home} exact/>
<Route path="/detail" component={Detail} />
</BrowserRouter>
</Provider>
);
}
}
複製代碼
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getHomeList } from '../../store/actions/home';
class Home extends Component {
componentDidMount() {
this.props.fetchHomeList();
}
state = {}
render() {
return ( <div>
home
length: { this.props.homeList.length }
</div> );
}
}
// 獲取數據
// state:整個store,home頁面,只要home模塊,過濾一下
// 過濾完結果(return)都會由 connect 傳給你組件的 props
const mapStateToProps = (state) => {
return {
homeList: state.home.homeList
}
}
// 用戶操做UI引發頁面變化
// 發起一個action
// dispacth行爲connect傳給組件
const mapDispatchToProps = (dispatch) => {
return {
fetchHomeList() {
dispatch(getHomeList)
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
複製代碼