騷操作!在react中使用vuex

菜菜_張發表於2019-02-26

原文地址

前言

筆者最近在學習使用react,提到react就繞不過去redux。redux是一個狀態管理架構,被廣泛用於react專案中,但是redux並不是專為react而生,兩者還需要react-redux建立一座橋樑。同時,redux架構規定只能傳送同步action,要想傳送非同步action就需要結合中介軟體如redux-thunkredux-saga等,所以說要想搞定redux還真是不容易啊,光名詞就這麼多。筆者以前也接觸過一點vuex,vuex對筆者這樣的菜雞相對友好,但是vuex是和vue配套的,是不可能用在react中的,這輩子都別想用在react中。但是我不服,那麼這篇文章就探索下如何製作一個可以在react中使用的類似vuex的狀態管理工具,我將它取名為reux

vuex <=> redux  + react-redux + redux-saga
複製程式碼

正文

  • 響應式資料觀測系統

vue的一大特色就是響應式資料觀測系統,它可以在get資料時收集依賴,在set資料時觸發更新。vuex藉助於vue的資料觀測系統,可以輕鬆的收集資料依賴,並且依賴可以精細到元件的粒度,也就是說某一狀態改變時,只有依賴到這一狀態的元件才會觸發rerender,這樣看來redux體系就比較傻,只要提交action,就會從根元件rerender(react-redux內部自動進行shouldCompoentUpdate判斷)。

vuex
上圖來自於vue官網對vuex架構的說明,連結
上圖中的componentvue component,只要vue component執行render,那麼vuex的資料響應系統就可以自動的收集依賴,當狀態改變時,依賴於此狀態的元件就會重新渲染。既然我們要實現的是一個類vuex的狀態管理工具,即支援以get的方式收集依賴,以set的方式觸發更新,所以reux利用了vue的響應式資料觀測系統,正所謂前人種樹,後人乘涼。

  • 如何收集依賴

我們已經有了響應式資料系統,接下來要解決的問題就是如何收集依賴,收集依賴必須要觸發get,而觸發get的前提是元件可以拿到store,因此第一步是向元件注入store。類似react-redux,reux提供了Provider使子元件可以拿到store。

class Provider extends Component {
  getChildContext() {
    return {store: this.props.store};
  }

  render() {
    const { children } = this.props;
    return children;
  }
}
Provider.childContextTypes = {
  store: PropTypes.object
};
複製程式碼

相應的子元件可以context拿到store,如下

class Child extends Component {
  render() {
    // store => this.context.store
  }
}
Child.contextTypes = {
  store: PropTypes.object
};
複製程式碼

這樣寫的缺點顯而易見,每個子元件都需要定義contextTypes,同樣的類似於react-redux,reux提供了connect函式,用於對映state => props

const connect = (mapStateToProps = () => {}) => {
  return (WrappedComponent) => {
    const Wrapper = class extends Component {
      render() {
        const store = this.context.store;
        const props = Object.assign({}, this.props, mapStateToProps(store.state, this.props), {dispatch: store.dispatch, commit: store.commit});
        return <WrappedComponent {...props} />
      }
    }
    Wrapper.contextTypes = {
      store: PropTypes.object
    };
    reaturn Wrapper;
  }
}
複製程式碼

這樣一來,只要元件執行render方法,便會觸發get鉤子,從而使得store自動收集依賴,我們再想下依賴是什麼,其實依賴應該是元件例項,那麼當set鉤子觸發時,每個依賴(即元件例項)只要執行forceUpdate方法就可以達到rerender的效果。

但是問題是,get鉤子觸發時,如何確定依賴到底是誰呢?借鑑vue,我們定義一個stack,當componentWillMount時進棧,當componentDidMount時出棧

componentWillMount() {
  pushTarget(this);
}

componentDidMount() {
  popTarget(this);
}
複製程式碼

這樣當get鉤子觸發時,當前target就是目標依賴。同時應當注意,當元件update時應當重新收集依賴,因為update之後依賴關係很可能已經變化了

update() {
  // 清空依賴
  this.clear();
  pushTarget(this);
  this.forceUpdate(() => {
    popTarget(this);
  })
}
複製程式碼

至此,我們的小目標已經完成了,在react中使用vuex不再是夢!

相關文章