React+Reflux 實現元件間通訊
寫這篇文章,不是把官方的例子或者github的例子給大家敲一遍,而是想把自己學習遇到的問題重點突出,讓大家少走彎路。
首先,學習這篇文章的時候,需要有React基礎,本文分為兩部分主要講解Reflux,第一部分給出其基本原理,第二部分給出一個我認為比較易懂的例子來說明元件間通訊的具體實現方式。
1.reflux 基本工作原理
reflux是目前github上flux型別架構,比較流行的一種實現類庫,它可以使React元件互相通訊。React在不用Reflux的時候也能夠通訊,但是會比較繁雜,導致資料經過很多間接元件,會導致維護修改成本增高。在reflux的幫助下,元件間可以輕鬆實現通訊,不論元件間是上下級關係還是並列關係。reflux工作原理比較簡單,如果你和我一樣主要開發後臺程式或對後端開發也比較熟悉,那麼reflux理解起來會特別容易。簡單說,reflux主要工作模式就是pubsub,那麼它是怎麼實現訊息釋出和訂閱的,在reflux有幾個關鍵的角色,分別是:Actions,Stores,viewComponets,action在這裡就相當於請求,就是釋出訊息,那麼訊息釋出到哪裡呢?你猜對了,傳送到Stores,Store就像一箇中間人,一般在這裡,可以暫存資料,而且可以訪問後臺程式,將資料傳入後臺,後臺處理完再返回資料,所以這一層比較重要。Store接收到資料的下一步步奏,就是喚醒所有訂閱者,並給訂閱者傳送資料。最後當然是viewComponets,這就是react中的元件,這些元件想要收到訊息那麼需要訂閱store,收到訊息再進行別的處理。
下面用一段比較簡單的程式碼來介紹整個流程。
2.實用示例
前提,編寫這段程式碼的時候用brackets寫的,沒有用伺服器,直接在幾個檔案中寫的,所以執行程式碼的時候沒有必要實用伺服器。類庫下載,react.js可以到處下載,而reflux的下載,需要在github下載。
例子佈局很簡單,一個main.html 中有3三個div,從上到下的順序分別是:<div id="header"></div> ,<div id="main"></div> ,<div id=“footer”></div>,具體程式碼如下:
<div id="header" class="container">
</div>
<div id='main' class="container">
</div>
<div id="footer" class="container">
</div>
這三個div分別對應,頁面中header,body,footer,元件設計很簡單,header中放置元件Header,main中放置元件Body,footer中放置元件Footer,具體佈局如下:
通訊流程很簡單,Header元件,每點選一次按鈕,就把輸入框的訊息,傳送給Body元件,和Footer元件,而Body和Footer收到訊息則展示,如下圖:ReactDOM.render( <Header headerName="reflux Test"/> , document.getElementById("header") ); ReactDOM.render( <Body/> , document.getElementById("main") ); ReactDOM.render( <Footer/> , document.getElementById("footer") )
如圖所示從上到下,沒一個灰色框就代表一個元件,在輸入框中輸入內容,點選輸入顯示內容,就將訊息傳送出去了,下面body,footer收到就顯示出來。下面先具體看看reflux的Action,stores是怎麼實現的,將reflux程式碼放到js檔案中,具體實現如下:
/*通訊元件*/
//請求定義(action)
var HeaderActions = Reflux.createActions([
'clickShowInBody'
]);
//請求接收,以及響應定義(store)
var HeaderStore = Reflux.createStore({
/*可以新增成員變數*/
listenables:[HeaderActions] /*可以同時監聽多個請求,所以是陣列*/
,
onClickShowInBody:function(model){//注意model為呼叫aciton傳過來的引數
//收到請求的回撥函式
console.log(model);
var returnData;
//1.列印請求傳過來的資料
//2.拿著資料做處理
returnData = model;
//3.傳送return的資料給訂閱該事件元件
this.trigger(returnData);
}
});
程式碼如上,是不是特別簡單,對的,這時最簡單的一種實現,但是麻雀雖小五臟俱全。
1.我們首先解釋一下Actions的實現:首先需要呼叫Reflux的建立Action的方法,明顯引數是一個數組,所以可以定義多個請求,明顯我們的HeaderActions比較簡單。傳送訊息的時候 呼叫形式如下 actionsName.XXXXX(data),例如HeaderActions.clickShowInBody("123"),的含義就是,傳送action請求,並攜帶資料“123”。
2.下面我們解釋一個Stores的實現,他負責請求的接收和響應:首先需要呼叫Reflux建立Store的方法,開始我們可以看到有一個listenables:[HeaderActions],明顯意思就是監聽,HeaderActions,而下面的onClickShowInBody 則是Actions中clickShowInBody的回撥方法,reflux規定,回撥方法同意命名為on+actionName ,並且actionName第一個字母大寫。如果store接收到訊息,那麼就會進入回撥方法中,並且入參model為action攜帶的資料。下面就是觸發監聽該Store的元件,將資料傳送到元件,那麼呼叫trigger(data)函式就可以,這樣就可以出發監聽的所有元件接收資料。reflux怎麼工作應該已經聊明白了,那麼下面我們具體看一下,reflux和React是怎麼協同工作的,下面看看,三個元件的具體實現。
程式碼很簡單,有幾個關鍵的地方:
1.首先是Header中發射請求的地方,就是handleClick方法中,headerActions.clickShowInBody(content),這行程式碼就是為了傳送action的,
2.其次就是對於Store的監聽,如Body和Footer元件中,有程式碼 Reflux.listenTo(HeaderStore,'handleEvent') ,其中第一個是引數是Store,第二是引數是,響應的回撥函式,handleEvent用來處理收到資料之後的事情。當然回到函式的名字可以隨便定義,但是還是定義為handle開頭,比較容易懂。
除了這兩點以外其他地方,都是熟悉的react程式碼。好了,這雖然只是一個簡單的例子,但是我認為也比較有代表性,就先聊到這裡,如果寫的有不對的地方,歡迎大家指正,謝謝!!!
var Header = React.createClass({
getInitialState:function(){
return {
headerName:"index"
}
}
,
handleClick:function(){
var content = eval(document.getElementById("edit")).value;
console.log(content);
HeaderActions.clickShowInBody(content);//傳送action請求
}
,
render:function(){
var headerName = this.props.headerName;
return (
<div className='panel panel-default'>
<h1>{headerName}</h1>
<h3>
<input id="edit" type="text"></input>
<button className ='btn btn-info btn-sm' onClick={this.handleClick}>輸入顯示內容</button>
</h3>
</div>
)
}
});
var Body = React.createClass({
mixins: [Reflux.listenTo(HeaderStore, 'handleEvent')],//監聽store,回撥方法名字為handleEvent
handleEvent:function(data){
console.log(data);
this.setState({
content:data
})
}
,
getInitialState:function(){
return {
content:"body"
}
}
,
render:function(){
var content = this.state.content;
return (
<div className='panel panel-default'>
<p>{content}</p>
</div>
)
}
});
var Footer = React.createClass({
mixins:[Reflux.listenTo(HeaderStore, 'handleEvent')], //監聽store,回撥方法名字為handleEvent
getInitialState:function(){
return {
content:'footer'
}
}
,
handleEvent:function(data){
this.setState({
content:data
});
}
,
render:function(){
var content = this.state.content;
return (
<div className='panel panel-default'>
<p>{content}</p>
</div>
)
}
});