摘要:原文地址沒想到這篇文章這么晚才出,最近發(fā)生了太多的事情,已致于心態(tài)全無,最終也離開了現(xiàn)在的公司,沒想到是這么的狼狽一個(gè)人的光芒可以放到很大也可以小到微乎其微,如果不能好好的規(guī)劃最終也只能默默的承受世上沒有相同的感同身受,感受真實(shí)才能真正的
原文地址:https://gmiam.com/post/react-...
沒想到這篇文章這么晚才出,最近發(fā)生了太多的事情,已致于心態(tài)全無,最終也離開了現(xiàn)在的公司,沒想到是這么的狼狽
一個(gè)人的光芒可以放到很大也可以小到微乎其微,如果不能好好的規(guī)劃最終也只能默默的承受
世上沒有相同的感同身受,感受真實(shí)才能真正的認(rèn)清一切
好了,下面步入正題
前面看到 Flux 架構(gòu)相對(duì)來說還是比較繁瑣,同時(shí)社區(qū)也涌現(xiàn)了很多第三方的框架模式,而 Redux 則脫穎而出
React 以組件的形式維護(hù)了一顆 UI 樹,但是對(duì)狀態(tài)數(shù)據(jù)沒有做更多的處理,Redux 則把狀態(tài)數(shù)據(jù)也抽象成了一棵樹來維護(hù)
它本身與 React 沒有直接關(guān)系,可以與其他框架配合使用,也可以很好的與 React 配合使用
Redux 的代碼量非常短小,核心只提供了 5 個(gè) API
createStore
combineReducers
bindActionCreators
applyMiddleware
compose
下面先來直觀的感受下 Redux
import { createStore } from "redux"; function counter(state = 0, action) { switch (action.type) { case "INCREMENT": return state + 1; case "DECREMENT": return state - 1; default: return state; } } let store = createStore(counter); store.subscribe(() => console.log(store.getState()) ); store.dispatch({ type: "INCREMENT" }); // 1 store.dispatch({ type: "INCREMENT" }); // 2 store.dispatch({ type: "DECREMENT" }); // 1
表象可以看出入口是 createStore,接收一個(gè)函數(shù)(這里叫做 reducer),這個(gè)函數(shù)接收 state 與 action 倆個(gè)參數(shù),然后 dispatch 一個(gè)對(duì)象(這里叫 action ,要包含一個(gè) type 屬性標(biāo)明行為),reducer 函數(shù)就會(huì)被觸發(fā)執(zhí)行來操作狀態(tài),同時(shí)也會(huì)觸發(fā) subscribe 訂閱的回調(diào),回調(diào)可以通過 store.getState() 獲取當(dāng)前狀態(tài)數(shù)據(jù)
到這里都很簡(jiǎn)單,那么如果我們需要處理的數(shù)據(jù)和狀態(tài)越來越多 reducer 函數(shù)就會(huì)越來越大導(dǎo)致難以維護(hù),所以 Redux 提供了 combineReducers 來處理這種情況,它把這個(gè)大的 reducer 分解成一個(gè)個(gè)小的 reducer ,每個(gè)小 reducer 維護(hù)自己的狀態(tài)數(shù)據(jù),這樣就分解出了一個(gè)狀態(tài)樹
做下變種
reducers/todos.js
export default function todos(state = [], action) { switch (action.type) { case "ADD_TODO": return state.concat([action.text]) default: return state } }
reducers/counter.js
export default function counter(state = 0, action) { switch (action.type) { case "INCREMENT": return state + 1 case "DECREMENT": return state - 1 default: return state } }
reducers/index.js
import { combineReducers } from "redux" import todos from "./todos" import counter from "./counter" export default combineReducers({ todos, counter })
App.js
import { createStore } from "redux" import reducer from "./reducers/index" let store = createStore(reducer) console.log(store.getState()) // { // counter: 0, // todos: [] // } store.dispatch({ type: "ADD_TODO", text: "Use Redux" }) console.log(store.getState()) // { // counter: 0, // todos: [ "Use Redux" ] // }
可以看到我們利用 combineReducers 把 reducer 做了拆分,combineReducers 部分精簡(jiǎn)源碼
export default function combineReducers(reducers) { var reducerKeys = Object.keys(reducers) var finalReducers = {} for (var i = 0; i < reducerKeys.length; i++) { var key = reducerKeys[i] if (typeof reducers[key] === "function") { finalReducers[key] = reducers[key] } } var finalReducerKeys = Object.keys(finalReducers) return function combination(state = {}, action) { var hasChanged = false var nextState = {} for (var i = 0; i < finalReducerKeys.length; i++) { var key = finalReducerKeys[i] var reducer = finalReducers[key] var previousStateForKey = state[key] var nextStateForKey = reducer(previousStateForKey, action) nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey } return hasChanged ? nextState : state } }
可以看到就是把對(duì)象中的 reducer 全部執(zhí)行一遍,把上次的狀態(tài)傳入進(jìn)去,最新的狀態(tài)返回回來,當(dāng)然你也可以提供自己的
combineReducers 方法
前面我們注意到 store.dispatch 都是一個(gè)純對(duì)象,也就是說我們的觸發(fā)都是同步的,如何支持異步?
下面我們來引入 Redux 中間件來增強(qiáng)下
import { createStore, applyMiddleware } from "redux"; import thunk from "redux-thunk"; import rootReducer from "./reducers/index"; function increment() { return { type: "INCREMENT_COUNTER" } } function incrementAsync() { return dispatch => { setTimeout(() => { dispatch(increment()); }, 1000) } } const store = createStore( rootReducer, applyMiddleware(thunk) ) store.dispatch(increment()) // 同步 store.dispatch(incrementAsync()) // 異步
同步方式的觸發(fā)跟以前是一樣的,這里的異步支持就是靠 Redux 的 applyMiddleware 中間件模式與 thunk 中間件做增強(qiáng)支持的
來看下 applyMiddleware 與部分 createStore 源碼
export default function applyMiddleware(...middlewares) { return (createStore) => (reducer, preloadedState, enhancer) => { var store = createStore(reducer, preloadedState, enhancer) var dispatch = store.dispatch var chain = [] var middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }
export default function createStore(reducer, preloadedState, enhancer) { if (typeof preloadedState === "function" && typeof enhancer === "undefined") { enhancer = preloadedState preloadedState = undefined } if (typeof enhancer !== "undefined") { return enhancer(createStore)(reducer, preloadedState) } .... return { dispatch, subscribe, getState, replaceReducer, [$$observable]: observable } }
createStore 里所謂的增強(qiáng)就是 applyMiddleware 一些中間件,
const store = createStore( rootReducer, applyMiddleware(thunk) )
與下面寫法是等效的
const store = applyMiddleware(thunk)(createStore)(rootReducer)
看上面 applyMiddleware 的源碼可以知道會(huì)先用 createStore 創(chuàng)建原始 store,然后把 getState 與 dispatch 傳給中間件,中間件處理完后返回?cái)U(kuò)展后的 store
看下 thunk 中間件源碼
function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === "function") { return action(dispatch, getState, extraArgument); } return next(action); }; } const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; export default thunk;
很簡(jiǎn)單傳入 dispatch, getState 后返回 next => action = > {...},然后傳入 store.dispatch 返回 action => {...} 即擴(kuò)展后的 dispatch
這個(gè)新的 dispatch 也是接受 action,如果是對(duì)象用原始 store.dispatch 直接觸發(fā),如果是函數(shù)則把 dispatch 傳進(jìn)函數(shù)體,把控制權(quán)交給函數(shù)內(nèi)部
注意后面執(zhí)行用到的 dispatch 已是擴(kuò)展后的能處理函數(shù)的 dispatch
回過頭來在說下 compose API,applyMiddleware 可以接受一系列中間件,內(nèi)部調(diào)用 compose 來做處理
compose(...chain) 等同于 (...args) => f(g(h(...args)))
也就是說傳入一組函數(shù),它會(huì)倒序執(zhí)行,把前一個(gè)的執(zhí)行結(jié)果傳給下一個(gè),達(dá)到漸進(jìn)增強(qiáng)效果
說到這里 Redux 和 它的 API 終于介紹差不多了,至于 bindActionCreators 后面介紹
說了這么多可以看到 Redux 自己就可以跑,那如何與 React 結(jié)合起來?那就需要 react-redux 這個(gè)中間橋梁了
react-redux 提供了倆個(gè) API
Provider store
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
Provider 就是一個(gè) React 組件,它接收一個(gè) store 屬性,把 store 掛在 React 的 Context 上,這樣它的子組件不需要顯示的傳遞 store 就可以獲取到
看個(gè)例子
import { Provider } from "react-redux" const store = createStore(reducer) render(, document.getElementById("root") )
那么問題來了,可以獲取到 store 后呢,如何做交互以及 React 與 Redux 的溝通,這時(shí)候 connect API 就派上用場(chǎng)了
還是繼續(xù)看個(gè)例子
import { bindActionCreators } from "redux" const App = ({todos, actions}) => () const mapStateToProps = state => ({ todos: state.todos }) const mapDispatchToProps = dispatch => ({ actions: bindActionCreators(TodoActions, dispatch) }) export default connect( mapStateToProps, mapDispatchToProps )(App)
connect 的源碼執(zhí)行大概是這樣
export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) { return function wrapWithConnect(WrappedComponent) { class Connect extends Component { constructor(props, context) { this.store = props.store || context.store } render() { const mappedState = mapStateToProps(store.getState(), this.props) const mappedDispatch = mapDispatchToProps(store.dispatch, this.props) const mergedProps = { mappedState, mappedDispatch } this.renderedElement = createElement(WrappedComponent,mergedProps) return this.renderedElement } } } }
這里做了適當(dāng)?shù)暮?jiǎn)化,從這可以看出 connect 返回了一個(gè) Connect 組件獲取到 store,然后把 store.getState() 與 store.dispatch
傳遞給我們的 mapStateToProps 與 mapDispatchToProps 函數(shù),返回相應(yīng)的數(shù)據(jù)與方法通過 props 傳遞給 React 組件,這樣 React 組件就可以獲取到相應(yīng)數(shù)據(jù)展示,同時(shí)也可以通過 dispatch 觸發(fā) Redux store 的數(shù)據(jù)變動(dòng),Connect 組件在根據(jù)數(shù)據(jù)對(duì)比看是否需要重新渲染~
connect 實(shí)際的代碼比這復(fù)雜的多,內(nèi)部做了細(xì)致的淺數(shù)據(jù)對(duì)比以提升性能
對(duì)于 react-redux 這里還有一個(gè)潛規(guī)則,那就是展示組件與容器組件相分離,就是說只有容器組件處理數(shù)據(jù)與狀態(tài)與 Redux 溝通,
展示組件只做正常的 UI 渲染,可以從這里了解更多 http://redux.js.org/docs/basi...
再看下上面的
const mapDispatchToProps = dispatch => ({ actions: bindActionCreators(TodoActions, dispatch) })
會(huì)把傳入的函數(shù)或?qū)ο蟮拿恳粋€(gè)方法做下面的變形
function bindActionCreator(actionCreator, dispatch) { return (...args) => dispatch(actionCreator(...args)) }
這樣 React 組件調(diào)用對(duì)應(yīng)的 action 時(shí)就可以 dispatch 這個(gè) actionCreator 產(chǎn)生的數(shù)據(jù)
最終不管有沒有明白都可以看下 https://github.com/reactjs/re...
這個(gè)例子來加深下理解,以及目錄結(jié)構(gòu)的分工,當(dāng)然有興趣多了解一些例子就更好了
呼,這篇到這里終于算是寫完了,最后大家都加油吧!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/80802.html
摘要:前端每周清單半年盤點(diǎn)之與篇前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開發(fā)教程工程實(shí)踐深度閱讀開源項(xiàng)目巔峰人生等欄目。與求同存異近日,宣布將的構(gòu)建工具由遷移到,引發(fā)了很多開發(fā)者的討論。 前端每周清單半年盤點(diǎn)之 React 與 ReactNative 篇 前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn);分為...
摘要:前端進(jìn)階進(jìn)階構(gòu)建項(xiàng)目一配置最佳實(shí)踐狀態(tài)管理之痛點(diǎn)分析與改良開發(fā)中所謂狀態(tài)淺析從時(shí)間旅行的烏托邦,看狀態(tài)管理的設(shè)計(jì)誤區(qū)使用更好地處理數(shù)據(jù)愛彼迎房源詳情頁(yè)中的性能優(yōu)化從零開始,在中構(gòu)建時(shí)間旅行式調(diào)試用輕松管理復(fù)雜狀態(tài)如何把業(yè)務(wù)邏輯這個(gè)故事講好和 前端進(jìn)階 webpack webpack進(jìn)階構(gòu)建項(xiàng)目(一) Webpack 4 配置最佳實(shí)踐 react Redux狀態(tài)管理之痛點(diǎn)、分析與...
摘要:前端每周清單第期與模式變遷與優(yōu)化界面生成作者王下邀月熊編輯徐川前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開發(fā)教程工程實(shí)踐深度閱讀開源項(xiàng)目巔峰人生等欄目。 showImg(https://segmentfault.com/img/remote/1460000013279448); 前端每周清單第 51 期: React Context A...
摘要:原文地址由于只涉及層的處理,所以構(gòu)建大型應(yīng)用應(yīng)該搭配一個(gè)框架模式才能使后期維護(hù)成本相對(duì)較小正是官方給出的應(yīng)用架構(gòu),他推崇一種單向的數(shù)據(jù)流動(dòng)模式,看下圖感受下整個(gè)流程是用戶與層交互,觸發(fā)使用進(jìn)行分發(fā)觸發(fā)回調(diào)進(jìn)行更新更新觸發(fā)層事件層收到信號(hào)進(jìn) 原文地址:https://gmiam.com/post/react-... 由于 React 只涉及 UI 層的處理,所以構(gòu)建大型應(yīng)用應(yīng)該搭配一個(gè)框...
摘要:傳統(tǒng)框架的缺陷傳統(tǒng)框架的缺陷模型視圖控制器的縮寫即視圖用戶看到并與之交互的界面。即模型是管理數(shù)據(jù)很多業(yè)務(wù)邏輯都在模型中完成。在的三個(gè)部件中,模型擁有最多的處理任務(wù)。所有的狀態(tài),保存在一個(gè)對(duì)象里面唯一數(shù)據(jù)源。1、傳統(tǒng)MVC框架的缺陷 模型(model)-視圖(view)-控制器(controller)的縮寫 V即View視圖:用戶看到并與之交互的界面。 M即Model模型是管理數(shù)...
閱讀 3927·2021-11-22 09:34
閱讀 1501·2021-11-04 16:10
閱讀 1733·2021-10-11 10:59
閱讀 3281·2019-08-30 15:44
閱讀 2045·2019-08-30 13:17
閱讀 3455·2019-08-30 11:05
閱讀 752·2019-08-29 14:02
閱讀 2627·2019-08-26 13:34