react這麼熱門的框架也不介紹了,redux是一個單項數據流的小框架,固然不僅配合react,它起初是爲react而配的,如今面向全部了,好比ng-redux的項目。redux作爲react的標準搭配,大有超越flux的勢頭。今天show一個例子來入門redux。源碼在此。 (本文默認你已有react基礎,es6基礎)javascript
這個效果很簡單就是一個count計數,+++++的按鈕按一下就會+1;input裏面寫什麼,submit就會alert什麼內容,這個demo很雜亂,是我修改的官方counter的例子,加了一些實驗的東西,比較適合熟悉redux這個東西。html
你們都知道,react是由一個個組件構成的,每一個組件都是與其餘沒有關係的,數據的傳遞都是經過props。redux實現的單項數據流就是爲react量身定作的。它維護了一個store,處理一些東西,業務邏輯,數據都在這個store裏面,就把它當作一個操做數據的顯示數據的東西,這個東西把它看成props傳入react組件,而後就能夠用來顯示和操做數據了。這個react組件和日常寫的react組件差很少,可是裏面的處理數據是用store,也就是傳入的props處理。詳細的教程請戳redux中文api。java
有興趣的同窗能夠看一下個人另外一篇博文:redux源碼賞析react
1.首先看一下主頁的html,很簡單,沒什麼東西。裏面這個ul其實沒啥用的,哈哈哈。其實就是一個div,裏面的東西都讓react處理。引入一個bundle.js(webpack壓縮後的文件)webpack
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>react+redux</title> </head> <body> <div id="root"> <ul> <li>l1</li> <li>l2</li> <li>l3</li> <li>l4</li> <li>l5</li> </ul> </div> <script type="text/javascript" src="bundle.js"></script> </body> </html>
2.主要就是main.js裏的代碼git
import {render} from "react-dom"
import React from "react" import {Provider} from "react-redux" import configureStore from "./store/configureStore" import App from './containers/app.js' const store =configureStore(); render(( <Provider store={store}> <App /> </Provider> ),document.getElementById('root'));
咱們看它作了什麼。引入了render,React,Provider,和本身寫的配置store-configureStore,還有通過redux處理過的組件App。render只是es6裏的寫法,也能夠換成日常react的寫法,就不列出了。Provider也不用管,只知道redux就是這麼寫組件就能夠了,裏面把經過本身建立的store傳入到react組件,而後把處理過的App組件扔在那裏,就完了。主要咱們看它的store怎麼建立的,和App怎麼處理的。es6
3.先看store怎麼處理的。這裏是/store/configureStore.jsgithub
import {applyMiddleware,createStore} from "redux";
import thunk from "redux-thunk"; import reducer from "../reducers/reducer.js" const createStoreWithMiddleware=applyMiddleware(thunk)(createStore) export default function configuerStore(initialStore){ const store=createStoreWithMiddleware(reducer,initialStore); return store; }
它引入了中間件thunk,具體的做用就是讓action能夠經過函數的處理,這個中間件只有幾行。若是action是函數就執行它,交給下一個中間件,沒有了就繼續流程web
export default function thunkMiddleware({ dispatch, getState }) { return next => action => typeof action === 'function' ? // action 竟然是函數而不是 plain object? action(dispatch, getState) : //在中間件裏消化掉,讓該函數控制 dispatch 時機 next(action); //不然調用 next 讓其餘中間件處理其餘類型的 action }
接下來,能夠看到建立store的函數createStoreWithMiddleware(reducer,initialStore);傳入的reducer和initialStore,initialStore是初始的Store狀態,能夠隨便取結構,好比本例的store只有一個count字段,用來存儲count數字。reducer是重要的一點,它是執行業務邏輯的地方,它是一個純函數,任什麼時候候結果都是不變的(傳入相同的東西)。redux
4.reducer /reducers/reducer.js"
import {combineReducers} from "redux"
import { ADD,RED } from '../actions/action'
function counter(state=0,action){ switch(action.type){ case ADD: return state+1; case RED:return state-1; default:return state; } } const rootReducer=combineReducers({ counter }) export default rootReducer;
combineReducers也不用管,他是組合多個reducer的,用於拆分業務。action只是描述了有事情發生了這一事實,並無指明應用如何更新 state。而這正是 reducer 要作的事情。 reducer就是如何改變數據的一個東西,這個counter的函數就是一個reducer,它是經過看action的type的值來操做state的,若是action的type是ADD,state就+1,是RED就-1;要謹記 reducer 必定要保持純淨。只要傳入參數同樣,返回必須同樣。沒有特殊狀況、沒有反作用,沒有 API 請求、沒有修改參數,單純執行計算。這個action又是什麼?
5.action actions/action.js
export const ADD="ADD"
export const RED="RED" export function increment(){ return { type:ADD } } export function decrement(){ return { type:RED } } export function ince(){ return (dispatch,getState) =>{ //const {counter}=getState() dispatch(increment()); } } export function dece(){ return (dispatch,getState)=>{ //const {counter}=getState() dispatch(decrement()) } }
爲了簡單,我把官網例子的action改了,就留下加減兩個action。它是什麼呢?你如何改變數據就看它了,它定義的ADD,RED兩個常量,做爲type,redux規定action必須有type屬性,別的它無論。redux提供的api只有幾個,其中有dispatch和getState,dispatch是惟一改變state的api,getState是獲得當前state,因此dispatch(action)就是改變當前state的方法,咱們想增長count,就是dispatch(increment());咱們把它封裝成ince()傳遞到組件裏,讓它能夠直接改變state,也就是每次點擊+++++就執行一下ince()這樣就能夠了。這裏的ince()和dece()只是手動套了一層dispatch,其實在項目中帶有反作用的操做是在這裏執行,而不是在reducer裏面執行,包括執行異步 API 請求,它能夠不純淨。爲何它返回一個函數而上面的action返回一個對象都可以運行呢,就是由於上面的thunk middleware的做用了。
總結一下,這就是整個store了,它建立了一個store,傳入了reducer(怎麼根據action數據),reducer裏包含了action(改變什麼數據,具體怎麼改)。把這個store傳入組件App,就能經過props裏面的方法改變count了。
6.通過redux修飾的組件
看main.js裏面直接把封裝的App展示出來了,咱們看它怎麼處理的App /containers/app.js
import {bindActionCreators} from "redux"
import {connect} from "react-redux" import App from '../component/App.js' import *as Actions from '../actions/action' function mapStateToprops(state){ return { counter:state.counter } } function mapDispatchToProps(dispatch){ return bindActionCreators(Actions,dispatch) } export default connect(mapStateToprops,mapDispatchToProps)(App)
bindActionCreators,看一下它的源碼,它把全部的Action都封裝了。 bindActionCreator
把action裝上一層dispatch。
//將 actionCreator 跟 dispatch 綁定在一塊兒 let bindActionCreator => (actionCreator, dispatch) { return (...args) => dispatch(actionCreator(...args)); } function bindActionCreators(actionCreators, dispatch) { if (typeof actionCreators === 'function') { //若是是單個 actionCreator,綁定一詞 return bindActionCreator(actionCreators, dispatch); } //返回一個改造過的「函數組合」 return mapValues(actionCreators, actionCreator => bindActionCreator(actionCreator, dispatch) ) }
唯一使用 bindActionCreators
的場景是當你須要把 action creator 往下傳到一個組件上,卻不想讓這個組件覺察到 Redux 的存在,並且不但願把 Redux store 或dispatch傳給它。
這裏的App組件就是普通react組件,修飾它的方法就是connect。咱們看connect幹了什麼。
connect傳入兩個函數爲參,第一個參數的名字mapStateToprops,也很清楚了,你須要把state裏的什麼放到props裏,這裏就只有counter一個字段。第二個mapDispatchToProps,把dispatch傳入到props,讓組件能夠調用dispatch來改變數據的結構,然而有bindActionCreators,就不用dispatch了,並且咱們還在action裏封裝的ince(),裏面就是dispatch增長的action,也不用dispatch了。
把數據,怎麼修改數據傳到props裏,而後就能夠用了。
最後看一下App這個組件,怎麼用數據和怎麼修改數據呢?
7.APP /component/App.js
import {render} from "react-dom"
import React from "react" class App extends React.Component { constructor(props){ super(props); this.handleClick=this.handleClick.bind(this); this.onSubmit=this.onSubmit.bind(this); } handleClick(){ const {ince,dece,counter} = this.props; ince(); } onSubmit(event){ alert(this.refs.text.value); } render() { const {ince,dece,counter} = this.props; return( <form onSubmit={this.onSubmit}> <input ref='text' type="text" /> <div>{counter}</div><input type="button" onClick={this.handleClick} value="++++++++++" /> <button>submit</button> </form> ) } } class Bpp extends React.Component{ render(){ return ( <div>{this.props.fuk} </div>) } } class Cpp extends React.Component{ render(){ const {ince,dece,increment,decrement,counter} =this.props; const id=this.props.params.id return ( <div>this is cpp id爲{id}</div>) } } export default App;
Bpp和Cpp沒有顯示出來,只是用做實驗,刪掉也沒事。(我只是忘記刪了)
這裏寫組件是用es6的寫法寫的。es6封裝了原生js的prototype操做,然而它是個不徹底的語法糖,由於在裏面不能寫屬性,public property也不行。
引入了React,render至關於ReactDOM.render。
組件用extends關鍵字繼承React.Componet,其實還能夠用React.createClass,實際上是同樣的。後者仍是有不少react開發者使用的,由於它比較方便,爲啥說比較方便,後面再提。
看一下這個jsx,就是一個form表單,提交觸發submit事件,一個輸入框,一個+++++的按鈕,一個div裏面是{counter},這樣顯示數據。
值得一提的是const {ince,dece,counter} = this.props;把props傳的東西拿出來。拿出來什麼呢?數據和如何修改數據的東西。這裏只拿出了ince和dece,他們是咱們封裝的action,在裏面dispatch了,你就直接用它操做數據就能夠了。counter是咱們的數據,在div裏{counter}就顯示了。
onSubmit={this.onSubmit},submit執行自身定義的事件,彈出this.refs.text的值,也就是input的值。
還有注意的就是constructor裏面的東西了,這個東西是在class繼承的時候執行的,super(props);是必有的,若是不寫也會自動給你加上,就是執行一遍父類的constructor。要注意的是
this.handleClick=this.handleClick.bind(this);
this.onSubmit=this.onSubmit.bind(this);
這兩個必需要綁定一下this,否則在這兩個函數裏取不到this,這很坑的,並且還很麻煩。再加上es6的class功能並不完善,有的地方替代不了js的原型操做,這也就促成了很開發者用React.createClass寫,組件生命週期事件還能夠直接用,天然是比較方便的。
總結,這就是整個例子的代碼了,
這裏手繪了一幅圖,主程序分爲建立store和修飾app兩部分,主程序main.js將他們弄到一塊就完成了react-redux的例子。裏面用的一些api沒有詳細講解,詳情看上面給出的redux中文api的連接。
webpack裏面沒有用任何複雜的工具,僅僅是用babel轉換了一下es6,其餘就沒有什麼了。