看見一篇不錯的文章轉載,文章源地址:https://blog.csdn.net/zwp438123895/article/details/69374940javascript
一. State和 Propshtml
state是狀態機。前端
應該包括:那些可能被組件的事件處理器改變並觸發用戶界面更新的數據,譬如須要對用戶輸入,服務器請求或者時間變化等做出響應。java
不該該包括:計算所得數據、React組件(在render()裏使用props和state來建立它)、基於props的重複數據(儘量保持用props來作做爲惟一的數據來源,把props保存到state中的有效的場景是須要知道它之前的值得時候,由於將來的props可能會變化)。react
props: 父級向子級傳遞數據的方式。git
二. 有狀態組件和無狀態組件(純函數組件)es6
有狀態組件github
經過React.createClass或者es6的class繼承React.Component建立的組件。特性:具有完整的生命週期及實例化過程、支持this及ref指向。web
無狀態組件:編程
即statelesscomponent( pure function Component)。以函數返回值方式方式建立的組件。特色: 無實例化過程及生命週期、無this及ref指向、函數接受props及context兩個參數。
實踐模式
建立多個只負責渲染數據的無狀態(stateless)組件,在他們的上層建立一個有狀態(stateful)組件並把它的狀態經過props傳給子級。有狀態的組件封裝了全部的用戶交互邏輯,state中處理狀態的變化, 而這些無狀態組件只負責聲明式地渲染數據.
三. 受控組件、非受控組件及混合組件
有許多的web組件能夠被用戶的交互發生改變,好比:<input>,<select>。這些組件能夠經過輸入一些內容或者設置元素的value屬性來改變組件的值。可是,由於React是單向數據流綁定的,這些組件可能會變得失控:
1.一個維護它本身state裏的value值的<Input>組件沒法從外部被修改
2.一個經過props來設置value值的<Input>組件只能經過外部控制來更新。
受控組件:
一個受控的<input>應該有一個value屬性。渲染一個受控的組件會展現出value屬性的值。
一個受控的組件不會維護它本身內部的狀態,組件的渲染單純的依賴於props。也就是說,若是咱們有一個經過props來設置value的<input>組件,無論你如何輸入,它都只會顯示props.value。換句話說,你的組件是隻讀的。
在處理一個受控組件的時候,應該始終傳一個value屬性進去,而且註冊一個onChange的回調函數讓組件變得可變。
非受控組件
一個沒有value屬性的<input>就是一個非受控組件。經過渲染的元素,任意的用戶輸入都會被當即反映出來。非受控的<input>只能經過OnChange函數來向上層通知本身被用戶輸入的更改。
混合組件
同時維護props.value和state.value的值。props.value在展現上擁有更高的優先級,state.value表明着組件真正的值。
目的:
一、支持傳入值;
二、可控:組件外部修改props可改變input組件的真實值及顯示值;
三、非可控:輸入框中輸入值,可同時改變input組件的真實值及顯示值。
四. redux和dva
Redux
1.Actions、Reducers 和 Store
action能夠理解爲應用向 store 傳遞的數據信息(通常爲用戶交互信息)。在實際應用中,傳遞的信息能夠約定一個固定的數據格式,好比: Flux Standard Action。 dispatch(action) 是一個同步的過程:執行 reducer 更新 state -> 調用 store 的監聽處理函數。若是須要在 dispatch 時執行一些異步操做(fetch action data),能夠經過引入 Middleware 解決。
reducer實際上就是一個函數:(previousState, action) => newState。用來執行根據指定 action 來更新 state 的邏輯。reducer 不存儲 state, reducer 函數邏輯中不該該直接改變 state 對象, 而是返回新的state 對象。
store是一個單一對象,redux中只有惟一一個store實例。主要做用:
1.管理應用的 state
2.經過 store.getState() 能夠獲取 state
3.經過 store.dispatch(action) 來觸發state 更新
4.經過 store.subscribe(listener) 來註冊state 變化監聽器
b. Dva
數據的改變發生一般是經過用戶交互行爲或者瀏覽器行爲(如路由跳轉等)觸發的,當此類行爲會改變數據的時候能夠經過 dispatch
發起一個action,若是是同步行爲會直接經過 Reducers
改變 State
,若是是異步行爲(反作用)會先觸發 Effects
而後流向 Reducers
最終改變 State
,因此在 dva 中,數據流向很是清晰簡明,而且思路基本跟開源社區保持一致(也是來自於開源社區)。
type State= any
State 表示 Model的狀態數據,一般表現爲一個 javascript 對象(固然它能夠是任何值);操做的時候每次都要看成不可變數據(immutabledata)來對待,保證每次都是全新對象,沒有引用關係,這樣才能保證 State 的獨立性,便於測試和追蹤變化。
在 dva 中你能夠經過 dva 的實例屬性 _store
看到頂部的 state數據,可是一般你不多會用到:
constapp=dva();
console.log(app._store); // 頂部的 state 數據
typeAsyncAction = any
Action 是一個普通 javascript對象,它是改變 State 的惟一途徑。不管是從 UI 事件、網絡回調,仍是 WebSocket 等數據源所得到的數據,最終都會經過 dispatch 函數調用一個 action,從而改變對應的數據。action 必須帶有 type
屬性指明具體的行爲,其它字段能夠自定義,若是要發起一個 action 須要使用 dispatch
函數;須要注意的是 dispatch
是在組件 connect Models之後,經過 props 傳入的。
dispatch({
type: 'add',
});
typedispatch = (a: Action) => Action
dispatching function 是一個用於觸發 action 的函數,action 是改變 State 的惟一途徑,可是它只描述了一個行爲,而 dipatch 能夠看做是觸發這個行爲的方式,而 Reducer 則是描述如何改變數據的。
在 dva 中,connect Model 的組件經過 props 能夠訪問到 dispatch,能夠調用 Model 中的 Reducer 或者Effects,常見的形式如:
dispatch({
type:'user/add', // 若是在 model 外調用,須要添加 namespace
payload: {}, // 須要傳遞的信息
});
typeReducer<S, A> = (state: S, action: A) => S
Reducer(也稱爲 reducing function)函數接受兩個參數:以前已經累積運算的結果和當前要被累積的值,返回的是一個新的累積結果。該函數把一個集合歸併成一個單值。
Reducer 的概念來自因而函數式編程,不少語言中都有 reduce API。如在 javascript 中:
[{x:1},{y:2},{z:3}].reduce(function(prev, next){
returnObject.assign(prev, next);
})
//return {x:1, y:2, z:3}
在 dva 中,reducers 聚合積累的結果是當前 model 的 state 對象。經過actions 中傳入的值,與當前 reducers 中的值進行運算得到新的值(也就是新的 state)。須要注意的是 Reducer 必須是純函數,因此一樣的輸入必然獲得一樣的輸出,它們不該該產生任何反作用。而且,每一次的計算都應該使用immutabledata,這種特性簡單理解就是每次操做都是返回一個全新的數據(獨立,純淨),因此熱重載和時間旅行這些功能纔可以使用。
Effect 被稱爲反作用,在咱們的應用中,最多見的就是異步操做。它來自於函數編程的概念,之因此叫反作用是由於它使得咱們的函數變得不純,一樣的輸入不必定得到一樣的輸出。
dva 爲了控制反作用的操做,底層引入了redux-sagas作異步流程控制,因爲採用了generator的相關概念,因此將異步轉成同步寫法,從而將effects轉爲純函數。至於爲何咱們這麼糾結於 純函數,若是你想了解更多能夠閱讀Mostlyadequate guide to FP,或者它的中文譯本JS函數式編程指南。
Subscriptions 是一種從 源 獲取數據的方法,它來自於 elm。
Subscription 語義是訂閱,用於訂閱一個數據源,而後根據條件 dispatch須要的 action。數據源能夠是當前的時間、服務器的 websocket 鏈接、keyboard 輸入、geolocation 變化、history 路由變化等等。
importkeyfrom'keymaster';
...
app.model({
namespace:'count',
subscriptions: {
keyEvent(dispatch) {
key('⌘+up, ctrl+up', () => { dispatch({type:'add'}) });
},
}
});
這裏的路由一般指的是前端路由,因爲咱們的應用如今一般是單頁應用,因此須要前端代碼來控制路由邏輯,經過瀏覽器提供的 HistoryAPI 能夠監聽瀏覽器url的變化,從而控制路由相關操做。
dva 實例提供了 router方法來控制路由,使用的是react-router。
import { Router, Route } from'dva/router';
app.router(({history}) =>
<Router history={history}>
<Route path="/" component={HomePage} />
</Router>
);
在組件設計方法中,咱們提到過Container Components,在 dva 中咱們一般將其約束爲 Route Components,由於在 dva 中咱們一般以頁面維度來設計 Container Components。
因此在 dva 中,一般須要 connect Model的組件都是 Route Components,組織在/routes/
目錄下,而/components/
目錄下則是純組件(Presentational Components)。
五. hoc
HOC(全稱Higher-ordercomponent)是一種React的進階使用方法,主要仍是爲了便於組件的複用。HOC就是一個方法,獲取一個組件,返回一個更高級的組件。
在React開發過程當中,發現有不少狀況下,組件須要被"加強",好比說給組件添加或者修改一些特定的props,一些權限的管理,或者一些其餘的優化之類的。而若是這個功能是針對多個組件的,同時每個組件都寫一套相同的代碼,明顯顯得不是很明智,因此就能夠考慮使用HOC。
一個最簡單的HOC實現是這個樣子的:
HOC能夠作什麼?
代碼複用,代碼模塊化
增刪改props
渲染劫持
增刪改props
能夠經過對傳入的props進行修改,或者添加新的props來達到增刪改props的效果。
好比你想要給wrappedComponent增長一個props,能夠這麼搞:
這樣,你就能夠在你的組件中使用message這個props:
渲染劫持
這裏的渲染劫持並非你能控制它渲染的細節,而是控制是否去渲染。因爲細節屬於組件內部的render方法控制,因此你沒法控制渲染細節。
好比,組件要在data沒有加載完的時候,現實loading...,就能夠這麼寫:
這個樣子,在父級沒有傳入data的時候,這一起就只會顯示loading...,不會顯示組件的具體內容
六. 項目中的實踐
1.合理使用有狀態組件及無狀態組件。在使用redux或者dva的場景下,理論上全部的組件均可以封裝爲無狀態組件(少數須要生命週期控制或者上文提到的混合式組件除外),model中封裝數據、異步effects及同步reducers,經過connect綁定到對應的組件上。
最佳實踐: router中getcomponent中定義的組件咱們稱之爲路由組件,通常路由組件會經過connect綁定model中定義的state及組件中定義的方法到該組件的props上。其餘方式定義的爲非路由組件,非路由組件儘可能避免使用connect,而是經過路由組件或者其餘上層經過props傳遞數據進行渲染。
2.理解subscription, effects及reducers中各自的功能職責。
3.package.json中定義的dependency,須要深刻研究,避免重複造輪子。
4.全局觀及合理的組件規劃。