redux源码学习与实现

678 阅读2分钟

「这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战

createStore

createStore利用发布订阅模式将数据保存,修改,并通知订阅订阅方法;

问题1

什么是发布订阅?

谁发布,谁订阅


function createStore(reducer) {
  let currentState = null;
  let dispatchListener = [];
  function getState() {
    return currentState;
  }
  function dispatch(action) {
    currentState = reducer(currentState, action);
    dispatchListener.forEach((listener) => listener());
  }
  function subscribe(listener) {
    dispatchListener.push(listener);
  }
  dispatch('___redux-------');
  return {
    getState,
    dispatch,
    subscribe,
  };
}

export default createStore;

applyMiddleware

// 本函数是干嘛的?
// 增强dispatch的
export default function applyMiddleware(...middlewares) {
  //不确定有多少个中间件,所以使用...对象展开符
  //再看看这函数在createStore文件中的用法,
  //  enhancer(createStore)(reducer) 函数柯里化
  // 看看,这是人想出来的用法吗???
  // 人家调用两次,你不得返回两层函数吗?
  // 所以就有了一下代码
  return (createStore) => {
    return (reducer) => {
      //有了createStore和reducer,是不是可以得到store?
      // 这行代码总是很眼熟的吧
      const store = createStore(reducer);
      //为啥要拿store?因为要增加dispatch啊,dispatch在store中呀

      //然后呢??到这里其实我是没思路的,但是看看applyMiddleware的参数,middleware还没用呢,所以下面的代码应该和middleware有关;
      let dispatch = store.dispatch;
      // 然后执行middleware
      const middlewareAPI = {
        getState: store.getState,
        dispatch: (action, ...args) => dispatch(action, args),
      };

      //执行的时候,里面需要getState和dispatch方法,所以将这两个参数传入中间件;
      console.log('middleware', middlewares);
      const chain = middlewares.map((middleware) => middleware(middlewareAPI));

      // 再执行一遍,又是函数柯里化
      dispatch = compose(...chain)(store.dispatch);
      console.log(typeof dispatch, dispatch);
      return {
        ...store,
        dispatch,
      };
    };
  };
}

function compose(...funcs) {
  if (funcs.length === 0) return (args) => args;
  if (funcs.length === 1) return funcs[0];
  return funcs.reduce((a, b) => {
    return (...args) => {
      return a(b(...args));
    };
  });
}



放在一个文件中,体会一下


function reducer(state = 1, action) {
  switch (action.type) {
    case 'add':
      return state + 1
    default:
      return state
  }
}

function thunk({ dispatch, getState }) {
  return function (next) {
    return function (action) {
      if (typeof action === 'function') {
        return action(dispatch, getState)
      }
      return next(action)
    }
  }
}

function applyMiddleware(...middlewares) {
  return function (createStore) {
    return function (reducer) {
      const store = createStore(reducer)
      let dispatch = store.dispatch
      const middleAPI = {
        dispatch: (...args) => dispatch(...args),
        getState: store.getState,
      }
      const chian = middlewares.map((middleware) => middleware(middleAPI))
      dispatch = compose(...chian)(store.dispatch)
      return {
        ...store,
        dispatch,
      }
    }
  }
}

function compose(...funcs) {
  if (funcs.length === 0) return (args) => args
  if (funcs.length === 1) return funcs[0]
  return funcs.reduce(
    (a, b) =>
      (...args) =>
        a(b(...args))
  )
}

// createStore
function createStore(reducer, enhancer) {
  if (enhancer) {
    return enhancer(createStore)(reducer)
  }
  let currentState = undefined
  let dispatchListener = []
  function getState() {
    return currentState
  }
  function dispatch(action) {
    currentState = reducer(currentState, action)
    dispatchListener.forEach((listener) => listener())
  }
  function subscribe(listener) {
    dispatchListener.push(listener)
    return function () {
      const idx = dispatchListener.indexOf(listener)
      dispatchListener.splice(idx, 1)
    }
  }
  dispatch({ type: 'redux-init' })

  return {
    getState,
    dispatch,
    subscribe,
  }
}

const store = createStore(reducer, applyMiddleware(thunk))
store.subscribe(() => {
  result = store.getState()
  console.log('result2', result)
})
let result = store.getState()
console.log('result1', result)
store.dispatch({ type: 'add' })
store.dispatch((dispatch) => {
  console.log('我执行了吗?', dispatch)
  setTimeout(() => {
    dispatch({ type: 'add' })
  }, 1000)
})