Redux----Regular的Redux實現整理

Regular的Redux實現整理

什麼問題?
組件的樹形結構決定了數據的流向,致使的數據傳遞黑洞javascript

怎麼解決?
全部組件都經過中介者傳遞共享數據
方案:
中介者:html

(function createStore() {
    var store;
    return function() {
            if(!store) {
                store = new Regular;    
            }
            return store;
    }
})()

組件A修改數據前端

define(['./store.js'], function(createStore) {
    var A = Regular.extend({
      name: "組件A",
      data: {
        title: '標題'
      },
      getData: function() {
         this.data.title = createStore().data.title;
      },
      setData: function() {
        store.data.title = '新標題'
        //通知全部其餘組件
        store.$emit('change', {
            title: '新標題'
        })
      }
    });
    return A;
});

其餘組件能夠監聽,也能夠主動拿:java

define(['./store.js'], function(store) {
    var B = Regular.extend({
      name: "組件B",
      init: function() {
        createStore().$on('change', function(newTitle){
            this.data.title = newTitle
        })
      }
    });
    return B;
});

兩個問題:
1 store.data能夠直接被訪問和修改->data和store分開&經過接口拿
2 只須要訂閱和派發(派發的時候會把數據存起來並通知其餘訂閱者)git

(function createStore() {
  var store;
  return function() {
    if(!store) {
        var store = new Regular;
                var state = {};
        store.getState = function(){ return state; };
        store.subscribe = function(listener) { store.$on('change', listener); }
        store.dispatch = function(action) {    
                  if(action.type == 'changeTitle') {
                       state.title = action.data.title;
                  }
          store.$emit('change', state);
        }
    }
    return store;
  }
})()

define(['./store.js'], function(createStore) {
    var A = Regular.extend({
      name: "組件A",
      data: {
        title: '標題'
      },
      getData: function() {
         this.data.title = createStore().data.title;
      },
      setData: function() {
                store.dispatch({
                         type: 'changeTitle', 
                         data: {title: '新標題'}
                })
    });
    return A;
});

define(['./store.js'], function(store) {
    var B = Regular.extend({
      name: "組件B",
      init: function() {
        createStore().subscribe(mapState)
      },
          mapState: function(state) {
             this.data.title = state.title
          }
    });
    return B;
});

這個就是基本的redux雛形。後面的其實都是一些改進。
改進1: 數據處理耦合在store當中->抽出reducer並能傳入初始statees6

(function createStore(reducer, initState) {
  var store;
  return function() {
    if(!store) {
        var store = new Regular;
                var state = initState;
        store.getState = function(){ return state; };
        store.subscribe = function(listener) { store.$on('change', listener); }
        store.dispatch = function(action) {    
                  state = reducer(state, action);
          store.$emit('change', state);
        }
    }
    return store;
  }
})()

reducer長這樣:github

function reducer1(state, action) {
  switch(action.type) {
    case 'CHANGE_TITLE':
      //es6寫法
      //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator
      //return {
      //...state,
      //title: action.data.title
      //}
      return Object.assign({}, state, {title: action.data.title});
      break;
  }
}

reducer說是一個純函數,本質上就是它不改變輸入的參數,最後返回了一個新對象(規約)。也解決了可變數據結構的問題。
改進2:每一個組件都須要依賴store,而且須要調用store的subscribe和dispatch方法->建立頂層容器而且擴展它的組件的能力redux

const App = Regular.extend({
  name: 'App',
  template: `
    <StoreProvider>
      <A />
      <B />
    </StoreProvider>
  `
});

var StoreProvider = Regular.extend({
      template: '{#include this.$body}',
      config: function(){
        this.store = createStore();
      },
      modifyBodyComponent: function( component ){
        component.dispatch =  this.store.dispatch.bind(store)
        //把訂閱這個工做給作了
        this.store.subscribe(function () {
          var state = this.store.getState();
          component.mapState(state);
        }.bind(this));
        component.getState = this.store.getState.bind(store)
      }
});

而後子組件內就能夠這樣調用了:數組

define([], function() {
    var B = Regular.extend({
      name: "組件B",
      init: function() {
        this.subscribe(mapState)
      },
          mapState: function(state) {
             this.data.title = state.title
          }
    });
    return B;
});

改進3: 這裏的store無法傳reducer和初始狀態,由於你是裏面調用的->createStore在外面作,而後把store傳給頂層容器數據結構

const App = Regular.extend({
  name: 'App',
  template: `
    <StoreProvider store={store}>
      <A />
      <B />
    </StoreProvider>
  `
  config(data) {
     data.store = createStore(reducers, { title: "標題" })
   }
});


var StoreProvider = Regular.extend({
      template: '{#include this.$body}',
      config: function(data){
        this.store = data.store;
      },
      modifyBodyComponent: function( component ){
        component.dispatch =  this.store.dispatch.bind(store)
        //把訂閱這個工做給作了
        this.store.subscribe(function () {
          var state = this.store.getState();
          component.mapState(state);
        }.bind(this));
        //這個方法其實不用了。由於數據經過connect得到了,而初始數據經過一開始就傳到store進去了
        component.getState = this.store.getState.bind(store)
      }
});

改進3: 而且這樣每一個組件都有一個mapState方法,並且作得事都比較相似就是把state的數據過來-> 抽出一個connect函數

function connect(config, B) {
    B.implement({ 
        mapState: function(state) {//this指向B
          const mappedData = config.mapState.call(this, state);
          mappedData && Object.assign(this.data, mappedData);
        }
    });
}

connect({
  //es6簡寫
  //mapState(state) { 
  //...
  //}
  mapState:  function(state) {
    return {
      title: state.title 
    }
  },  A);

以上,就是redux核心的基本實現原理。

中間件是幹嗎用的?
https://guoyongfeng.github.io/book/15/04-redux-logger%E7%9A%84%E5%BA%94%E7%94%A8%E5%92%8C%E7%90%86%E8%A7%A3.html
好比上面的logger中間件,就是想在store的dipatch方法裏面作點其餘事,好比打印下個性化的日誌。
怎麼實現?

正常想法:代理dispatch方法

function applyMiddleware() {
  let store = createStore(reducer1, initState);
  store.dispatch = function(action) {
    console.log("....")
    store.dispatch(action);
    console.log("....")
  }
  return store;
}

後面呢也是改進。。
改進1:不但願改變原先store&dispatch內打印日誌部分但願能作其餘事情->傳遞一個middleware回調進去

function logger(dispatch, action) {
    console.log("....")
    dispatch(action);
    console.log("....")
}

function applyMiddleware(middleware) {
  let store = createStore(reducer1, initState);
  let dispatch = function(action) {
      middleware(store.dispatch,  action)
  }
  return Object.assign({}, store, {dispatch: dispatch});
}

var store = applyMiddleware(logger)

改進2: 如何處理多箇中間件(每一箇中間件作的事不同)並且store的dispatch應該只被執行一次

var store = applyMiddlewares(logger,  someMiddleware)
function applyMiddlewares(logger, someMiddleware) {
  let store = createStore(reducer1, initState);
  let dispatch = function(action) {
      //但願一層層代理地執行中間件,最左邊的先執行
      someMiddleware(logger(store.dispatch, action), action)
  }
  return Object.assign({}, store, {dispatch: dispatch});
}

//因此logger必須return一個函數,給其餘middleware執行
function logger(dispatch) {
   return function(action) {
        console.log("....")
        dispatch(action);
        console.log("....")
   }
}

改進3: 仔細看這裏面的實現發現就是數組的reduceRight方法

function applyMiddlewares(middlewares) {
  let store = createStore(reducer1,  initState);
  let dispatch =  middlewares.reduceRight(function(dispatch, middeware) {
        return middeware(dispatch);
  }, store.dispatch)
  return Object.assign({}, store, {dispatch: dispatch});
}

改進4: 中間件裏面的next函數是幹嗎用的?->其實就是傳進去的dispatch方法

function logger(next) {
   return function(action) {
        console.log("....")
        next(action);
        console.log("....")
   }
}

以上,就是redux中間件實現的基本原理。

Action Creator是什麼?
是對dispatch函數參數(也就是action)的一種抽象,便於Action的複用
好比咱們這麼寫:

this.$dispatch({
    action: 'CHANGE_TITLE',
    data: { title: '新標題' }
})

可能其餘組件也須要寫類似的代碼,你須要複製代碼。其實咱們能夠抽出一個creator。

//這部分能夠被複用
const  CHANGE_TITLE = 'CHANGE_TITLE';
function changeTitle(newTitle) {
  return {
        type: CHANGE_TITLE ,
        data: { 
               title:  newTitle
        }
  }
}

this.$dispatch(changeTitle('新標題'))

最後的一張圖總結:

做者知乎/公衆號:前端瘋

相關文章
相關標籤/搜索