前言
這是一個系列文章,旨在分享在react及相關技術棧實踐過程中的個人感悟,心得。如果有不好的地方,歡迎各位批評指正。
由於對react本身還未深入瞭解,今天我們先來談一談redux相關的問題。
Who creates it?
Dan Abramov是redux
的作者,同時,他也是Create React App
, React Hot Loader
作者。當然1年前,他也由於redux
及相關的開源貢獻加入了facebook
(他大二就輟學了,之前還當過.net工程師)。
在我最初瞭解到他的時候,我覺得他非常有禮貌。同時,也為了更多的瞭解redux
,我計劃開始閱讀他的每一條tweet,原先計劃的是從15年7月開始,後來因為進展緩慢,而且react
版本也已經發生很大變化了,於是便從16年1月1日開始閱讀,目前記錄到7月15日了。事實也證明,在這個過程中,的的確確學習到了很多東西。包括redux
的文件及redux-links
的作者Mark Erikson,以及國外很多寫過redux
系列的朋友們。
如果你有興趣的話,可以看看我摘錄的一些片段。其中除了知識性的內容外,還有一些關於它自己生活,經歷,學習方法,如何面對JS疲勞等等的摘錄。也讓我漸漸的瞭解到了國外的程式設計師們的一些觀點,興趣,梗等等。
正文
好了,暫時先介紹到這裡了。切回redux本身,下面是學習原始碼過程中自己的一些體會。
createStore
createStore
的第3個引數為enhancer
,如果enhancer
有多個,那麼應該使用compose
的方式組合多個enhancer
且每個enhancer
的模板為export default createStore => (reducer, preloadedState, enhancer) => {...}
因為在createStore
中執行了:return enhancer(createStore)(reducer, preloadedState)
另外,上面的提到的形如(reducer, preloadedState, enhancer) => {...}
這個樣子的其實都可以叫做createStore
這也是社群有那麼多enhancer
的原因,他們可以形成一個enhancer
鏈,我呼叫你的createStore
,然後返回我的createStore
供下一級呼叫
所以在自己的createStore
的函式體中經常能看到諸如var store = createStore(reducer, preloadedState, enhancer);
這樣的用法,目的就是讓自己這一級之前的enhancer
產生一個store
出來,而之前的enhancer
裡的createStore
又會呼叫之前的,到最盡頭,就是redux
本身的createStore
applyMiddleware
applyMiddleware
的目的是返回一個enhancer
,這個enhancer
儲存了1個或者多箇中介軟體,中介軟體在上一級的dispatch
方法的基礎上增添自己的邏輯,然後返回自己的dispatch
方法
對於中介軟體而言,中介軟體的模板為:export default store => next => action => {...}
。有的地方也寫成export default _ref => next => action => {...}
或者export default ({getState, dispatch}) => next => action => {...}
,看自己喜好了
實際的呼叫順序如下(定義在redux
的applyMiddleware.js
中):
1. middleware(middlewareAPI);
/*
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
};
chain = middlewares.map(middleware => middleware(middlewareAPI));
dispatch = compose(...chain)(store.dispatch);
第1步即為第1次執行中介軟體,用redux自己的dipatch初始化各個中介軟體裡的dispatch(也就是中介軟體的next引數)和getState。
從而確保至少redux本身能夠正常工作,中介軟體的store或者_ref即為這裡的middlewareAPI
* */
2. dispatch = compose(...chain)(store.dispatch);
/*
第2步即為第2次執行中介軟體
即用compose的形式鏈式呼叫第1步返回的中介軟體集合,如果中介軟體是定義在applyMiddleware的最後一個
那麼中介軟體裡的next為store.dispatch,否則next為上一個中介軟體返回的結果,可以理解為上一個中介軟體
返回的是封裝了dispatch的自己的dispatch,這裡的原理其實和enhancer一模一樣
enhancer的目的是封裝多次createStore並用compose的方式進行呼叫
middleware的目的是封裝多次dispatch並用compose的方式進行呼叫
* */
總結:
/*
所以最後在redux的createStore.js中return的enhancer(createStore)(reducer, preloadedState)的結果就是一個增強
版的store,而這個增強版的store中存放的是增強版的dispatch
* */
/* ××××××××××××××××關於combineReducers×××××××××××××××
* 從執行上來說,combineReducers實際上最後就是變成對reducers進行深度優先遍歷並執行的過程
* 從結構上來說,combineReducers決定了我們的state狀態樹的最終結構或者說形狀,他是呈一個樹型結構的
* combineReducers(reducerA, reducerB),reducerA裡面巢狀combineReducers(reducerA-child1, reducerA-child2)
* 實際上對應狀態樹而已就是第一層有兩個節點A,B,而A節點下面有兩個子節點A-child1,A-child2
*
* 所以在最初設計的時候,我們要設想我們最終的狀態樹的樣子,然後合理劃分reducer,就像設計資料庫的表結構一樣。
當然這是比較概括的說法,事實上reducer的設計或者說state的劃分有太多太多值得研究的東西,這個我們以後再談了。
* */
bindActionCreator
bindActionCreator
實際就是給actionCreator
外層再新增了一層函式,而這層函式存放了對dispatch
的引用
function bindActionCreator(actionCreator, dispatch) {
return (...args) => dispatch(actionCreator(...args))
}
所以我們可以一般在元件裡直接呼叫bindActionCreator
返回的actionCreator
,即this.props.loadSomething(...)
。而不用寫成dispatch(actionCreator(...args))
,實際上他們是等價的
connect
既然提到了redux
,由於目前我是採用react
進行開發,所以不得不提到相關的react-redux
。其中最重要的莫屬於connect
這個函式了。
傳入connect
的元件在掛載到頁面上後會呼叫store.subscribe
進行訂閱,訂閱的目的是我們呼叫dispatch
的時候,表明我們的狀態樹即將發生變化,這個時候我們希望我們的元件對應發生變化,而元件變化的唯一方式就是setState
。
訂閱就是告訴redux
,這個元件是依賴於狀態樹的某部分工作的,所以當你變化的時候,記得獲取最新的state
,然後通知我,至於我如何響應,那就是我自己的事了,你只管通知我狀態樹發生了變化並把它傳給我就行了。值得一提的是,connect
內部進行了大量的效能優化,避免不必要的渲染,關於此以及mapStateToProps
和mapDispathToProps
,我們放到以後再談。
結語
篇幅有限,這一篇文章暫時就先這樣啦,更多的內容,我想放在下一篇來分享,同時自己也在不斷學習,希望能理解得更好。
值得一提的是,我們也許會認為我們瞭解到的redux,mobx,rxjs等等完全不同理念的庫,他們的作者也許也是”極端”的,是排斥他人及理念的。實際上,這是不正確的,早在16年5月,Dan就和mobx的作者在twitter上有過互動,他們達成了共識,那就是和對方一起合作,一起推動自身以及react的發展。
對於redux-thunk,文件中也許會首先建議使用這個簡單的庫來處理非同步相關的問題。對於複雜的應用,他們也推薦使用redux-saga這樣的庫去重構自己的程式碼。在twitter上,Dan也多次提到過庫的應用場景的問題,建議大家用之前先了解自己為什麼要使用,它解決了哪些痛點,然後再去使用。甚至特意提過issue,來了解react-router-redux的作用。
除此之外,也提到在時間充裕的情況下,學習react,應該先從本身入手,ES6,webpack,jsx,redux等等和react本身都是沒有直接聯絡的,在學習完react之後,我們知道了他本身的哪些不足,哪些地方需要加強,哪些地方需要引入第三方庫去解決,解決的是哪些痛點,我們再去了解這些工具,才能真正體會到他們的威力。
說到這裡,稍微有一點遠了,不過我覺得還是有必要提及一下。那就是,我們身處一個浮躁的社會,無論是在現實中對待朋友,親人,陌生人,由於學習,工作,生活的壓力,周遭的浮躁氛圍的影響,多多少少也會讓自己帶著些許暴戾之氣。在網路上,由於約束的放寬,我們也許更會將壓抑的情感釋放給廣袤的網路世界,在微博,貼吧,知乎上,我們或多或少書寫著,察覺著這樣的行為。
但是,作為一名程式猿,我還是期待能夠看到我們這個圈子更多的將時間,精力,努力花費在對現有技術的改進,對未知世界的探索,追尋程式,庫,框架,思想的本質,結交志同道合的朋友,一起交流,分享,思考對技術的看法。而不是捲入無休止的撕逼,用了某某而產生的優越,甚至借貶低他人來抬高自己。
我們可以理解一時的憤懣之情,因為我們大多,真是隻是普普通通的社會人,喜怒哀樂再平常不過。但若我們一直保持這種狀態,永遠在上面這些場景都留下對人不對事的話語,譏諷,甚至謾罵。希望大家能為我們的後代想想。