主要的核心在於connect,下面經過一個簡化版的connect來講下它的主要做用。react
在第一篇文章的時候說過,connect這個函數其實最終會返回一個包着渲染組件的高階組件。git
它的基礎做用以下:
一、從context裏獲取store
二、在componentWillMount 裏經過mapStateToProps獲取stateProp的值
三、在componentWillMount 裏經過mapDispatchToProps獲取dispatchProps的值
四、在componentWillMount 裏訂閱store的變化
五、將得到的stateProp,dispatchProps,還有自身的props合成一個props傳給下面的組件github
export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => { class Connect extends Component { static contextTypes = { store: PropTypes.object } constructor () { super() this.state = { allProps: {} } } componentWillMount () { const { store } = this.context this._updateProps() store.subscribe(() => this._updateProps()) } _updateProps () { const { store } = this.context let stateProps = mapStateToProps ? mapStateToProps(store.getState(), this.props) : {} // 防止 mapStateToProps 沒有傳入 let dispatchProps = mapDispatchToProps ? mapDispatchToProps(store.dispatch, this.props) : {} // 防止 mapDispatchToProps 沒有傳入 this.setState({ allProps: { ...stateProps, ...dispatchProps, ...this.props } }) } render () { return <WrappedComponent {...this.state.allProps} /> } } return Connect }
明白了上面以後,咱們就能夠來看真的connect長啥樣了。redux
connect接受四個參數:mapStateToProps,mapDispatchToProps,mergeProps,optiponssegmentfault
返回:一個注入了 state 和 action creator 的 React 組件緩存
具體的mapStateToProps,mapDispatchToProps,mergeProps,optipons如何使用能夠看官方文檔,
這裏簡單的說下~性能優化
傳入:state,ownProps
輸出:statePropsapp
這個很是關鍵,若是定義了這個參數,就會監聽redux store的變化,沒有的話,就不會。該回調函數必須返回一個純對象,這個對象會與組件的 props 合併。
同時,若是指定了第二個ownProps,這個參數的值爲傳入到組件的props,只要組件接受到新的props,mapStateToProps也會被調用ide
stateProps,dispatchProps,自身的props將傳入到這個函數中。
默認是Object.assign({}, ownProps, stateProps, dispatchProps)函數
pure = true ; // 默認值,這時候connector 將執行 shouldComponentUpdate 而且淺對比 mergeProps 的結果,避免沒必要要的更新。因此其實connect是有幫咱們作性能優化的。
最終使用:
connect(mapStateToProps, mapDispatchToProps, mergeProps)(MyComponent)
囉嗦了那麼多,看看Connect的源碼的目錄結構。
connect.js 就是將相關的參數傳給connectAdvanced.js
其中有個參數:
// if mapStateToProps is falsy, the Connect component doesn't subscribe to store state changes // 因此,若是mapStateToProps沒有傳的話,是不訂閱store更新的 shouldHandleStateChanges: Boolean(mapStateToProps),
重點看看connectAdvanced
其中有個比較重要的概念,selectorFactory 它的做用是負責運行選擇器函數,這些函數從state,props,dispatch裏計算出新的props。
例如:
export default connectAdvanced((dispatch, options) => (state, props) => ({ thing: state.things[props.thingId], saveThing: fields => dispatch(actionCreators.saveThing(props.thingId, fields)), }))(YourComponent)
當pure爲true的時候,返回的selector就會緩存它們各自的結果,這樣connectAdvance裏的shouldComponentUpdate就能夠對比this.props和nextProps,當沒有發生改變的時候返回false
不更新。當pure爲false的時候,shouldComponentUpdate在任什麼時候候都會返回true。
const selectorFactory = options.pure ? pureFinalPropsSelectorFactory : impureFinalPropsSelectorFactory
因爲impure每次都會返回新的object,因此每一次shouldComponentUpdate對比的時候都必定會返回true
export function impureFinalPropsSelectorFactory( mapStateToProps, mapDispatchToProps, mergeProps, dispatch ) { return function impureFinalPropsSelector(state, ownProps) { return mergeProps( mapStateToProps(state, ownProps), mapDispatchToProps(dispatch, ownProps), ownProps ) } }
而pureFinalPropsSelectorFactory 會緩存上一次stateProps, dispatchProps,mergeProps,當須要更新的時候,當那哪一部分須要更新的時候才更新
export function pureFinalPropsSelectorFactory( mapStateToProps, mapDispatchToProps, mergeProps, dispatch, { areStatesEqual, areOwnPropsEqual, areStatePropsEqual } ) { let hasRunAtLeastOnce = false let state let ownProps let stateProps let dispatchProps let mergedProps // 第一次跑的時候,就去得到mergedProps,而且將hasRunAtLeastOnce 設置成true, 下次跑的時候,就直接走handleSubsequentCalls function handleFirstCall(firstState, firstOwnProps) { state = firstState ownProps = firstOwnProps stateProps = mapStateToProps(state, ownProps) dispatchProps = mapDispatchToProps(dispatch, ownProps) mergedProps = mergeProps(stateProps, dispatchProps, ownProps) hasRunAtLeastOnce = true return mergedProps } // 若是props state都變化的時候,mapStateToProps是必定會變化的, // 而mapDispatchToProps會看ownProps是否存在 function handleNewPropsAndNewState() { stateProps = mapStateToProps(state, ownProps) if (mapDispatchToProps.dependsOnOwnProps) dispatchProps = mapDispatchToProps(dispatch, ownProps) mergedProps = mergeProps(stateProps, dispatchProps, ownProps) return mergedProps } function handleNewProps() { if (mapStateToProps.dependsOnOwnProps) stateProps = mapStateToProps(state, ownProps) if (mapDispatchToProps.dependsOnOwnProps) dispatchProps = mapDispatchToProps(dispatch, ownProps) mergedProps = mergeProps(stateProps, dispatchProps, ownProps) return mergedProps } // 若是隻是state改變的話,就只須要看mapStateToProps,而後他們還會比較先後 // 是否相等,若是不相等,就合併 function handleNewState() { const nextStateProps = mapStateToProps(state, ownProps) const statePropsChanged = !areStatePropsEqual(nextStateProps, stateProps) stateProps = nextStateProps if (statePropsChanged) mergedProps = mergeProps(stateProps, dispatchProps, ownProps) return mergedProps } // 不是第二次的時候,走這個函數 function handleSubsequentCalls(nextState, nextOwnProps) { // 首先對比nextOwnProps跟ownProps是否相等 const propsChanged = !areOwnPropsEqual(nextOwnProps, ownProps) // 對比nextState,state是否相等 const stateChanged = !areStatesEqual(nextState, state) // 比較完成賦值,下一次對比時候使用 state = nextState ownProps = nextOwnProps // 當props跟state都變化的時候,看handleNewPropsAndNewState if (propsChanged && stateChanged) return handleNewPropsAndNewState() if (propsChanged) return handleNewProps() if (stateChanged) return handleNewState() // 注意 若是state跟props都沒有發生改變的話,直接返回以前的mergedProps, // 組件不從新渲染 return mergedProps } return function pureFinalPropsSelector(nextState, nextOwnProps) { return hasRunAtLeastOnce ? handleSubsequentCalls(nextState, nextOwnProps) : handleFirstCall(nextState, nextOwnProps) } }
總結來講,當state變化的時候,mapStateToProps必定會被調用,而props變化的時候要看dependsOnOwnProps,計算出來以後,用mergProps更新。
更新以後的props在哪裏進行比較呢?
上面說的,其實都依賴於connect裏面的代碼幫忙,connect在contructor裏初始化selector
initSelector() { const sourceSelector = selectorFactory(this.store.dispatch, selectorFactoryOptions) this.selector = makeSelectorStateful(sourceSelector, this.store) this.selector.run(this.props) } function makeSelectorStateful(sourceSelector, store) { // wrap the selector in an object that tracks its results between runs. const selector = { // run其實作了兩個事情 //一、去計算nextProps的值 //二、若是有更新,設置shouldComponentUpdate=true 跟 props run: function runComponentSelector(props) { try { const nextProps = sourceSelector(store.getState(), props) // 注意這裏只是進行了一個淺比較,當不等的時候,shouldComponentUpdate = true if (nextProps !== selector.props || selector.error) { selector.shouldComponentUpdate = true selector.props = nextProps selector.error = null } } catch (error) { selector.shouldComponentUpdate = true selector.error = error } } } return selector }
接下來 生命週期的話仍是看react-redux裏的寫法吧~
componentDidMount() { //shouldHandleStateChanges 默認爲true if (!shouldHandleStateChanges) return // componentWillMount fires during server side rendering, but componentDidMount and // componentWillUnmount do not. Because of this, trySubscribe happens during ...didMount. // Otherwise, unsubscription would never take place during SSR, causing a memory leak. // To handle the case where a child component may have triggered a state change by // dispatching an action in its componentWillMount, we have to re-run the select and maybe // re-render. // 意思就是SSR的時候會觸發componentWillMount ,可是不會觸發componentDidMount , // componentWillUnmount,因此訂閱事件要放在componentDidMount裏面,不然, 就沒辦法取消訂閱 // 形成內存泄漏。 this.subscription.trySubscribe() this.selector.run(this.props) // 當shouldComponentUpdate= true的時候,強制刷新 if (this.selector.shouldComponentUpdate) this.forceUpdate() } componentWillReceiveProps(nextProps) { // 前面說過,this.selector.run有兩點做用: // 1 計算下一個props // 2 經過對比看看shouldComponentUpdate的值 this.selector.run(nextProps) } shouldComponentUpdate() { return this.selector.shouldComponentUpdate } componentWillUnmount() { // 取消訂閱,讓全部值都復原 if (this.subscription) this.subscription.tryUnsubscribe() this.subscription = null this.notifyNestedSubs = noop this.store = null this.selector.run = noop this.selector.shouldComponentUpdate = false }
最關鍵的是,onStateChange,當咱們訂閱了這個函數的時候,每一次dispatch都會觸發它,
onStateChange() { // 依舊是先經過selector獲取nextProps的值 this.selector.run(this.props) // 而後若是shouldComponentUpdate=true if (!this.selector.shouldComponentUpdate) { // 這個是啥意思?是這樣的,每一個Connect組件裏面都有一個subscription對象,它也是一個訂閱模型 // 每一個父的Connect訂閱的是 子Connect組件的onStateChange函數,而父的Connect的onStateChange // 函數,被誰訂閱呢?固然是store(redux)啦, 即流程爲 // dispathc(action)---觸發store的訂閱即父的onStateChange---父的onStateChange觸發即觸發子 // Connect的onStateChange this.notifyNestedSubs() } else { this.componentDidUpdate = this.notifyNestedSubsOnComponentDidUpdate this.setState(dummyState) } }
最後在render的時候
render() { const selector = this.selector selector.shouldComponentUpdate = false if (selector.error) { throw selector.error } else { // 將mege的props傳給wrappedComponent return createElement(WrappedComponent, this.addExtraProps(selector.props)) } }
最終
return hoistStatics(Connect, WrappedComponent)
想一想hoistStatics是什麼做用,它實際上就是相似Object.assign將子組件中的 static 方法複製進父組件,但不會覆蓋組件中的關鍵字方法(如 componentDidMount)。
好長啊我都有點累了。真是枯燥無聊又無趣,仍是用一個圖代表一下connect大體的流程吧。
說了那麼多,其實只要記住亮點就能夠了:
一、connect是一個函數,會返回一個connect的類,裏面包着咱們要渲染的wrappedComponent,而後將stateProps,dispatchProps,還有ownProps合併起來,一塊兒傳給wrappedComponent
二、connect其實幫咱們作了性能的優化的,當state跟props發生改變時,selector若是變化,最終計算出來的結果會進行一次淺比較來設置shouldComponentUpdate防止重複渲染
參考:
一、https://segmentfault.com/a/11...
二、https://github.com/reactjs/re...