前言

很久之前看过@砖家brickspert的完全解读redux,觉得写的非常棒,看完之后收获颇多。
昨天想回忆一下redux,具体细节我又记得不清晰了,果然代码还是要自己试着实现一下才能记忆深刻,于是我对着砖家的文章,尝试着实现一下redux,主要为主体功能上的实现,不是像素级的还原redux源码。
后面我将会贴出自己的实现代码,尽量每行代码都打出自己的理解,以及一些可能你们觉得可能是无关紧要的话,有的直接略过就好,反正我脸皮厚^_^。
对了,建议先看砖家的redux文章再来看我写的代码,我怕我说的不是很清晰

以下为砖家的redux文章链接https://github.com/brickspert...

正文


//说实话我其实不太了解类这个东西,第一个学的语言就是js,js这方面视频教程又很少说这东西
//既然了解了,那就硬着头皮上呗,
class Store {
  //搞一个构造器 传入reducers,还有最初始的对象,
  //在实际的工作中 state可能被分成多块,person1需要一个reducer,person2也需要一个reducer
  //他们对应的管理自己的区域,那么这个reducers就是person1和person2的reducer集合
  // state={
  //   person1:{name:'xiaoming1',age:21},
  //   person2:{name:'xiaoming2',age:22},
  // }
  //reducers={
  //   person1:person1Reducer,
  //   person2:person2Reducer
  // }
  constructor(reducers, state = {}) {
    //先把传入的仓库拿出过来
    this.state = state
    //搞个报警器,谁动了仓库我就报警
    this.listeners = []
    //下面这个就是把一群reducer合并的调用代码,后面咱们再说
    this.reducer = this.combineReducers(reducers)
  }
  // 把仓库输出一下
  getState() {
    return this.state
  }
  //把报警器添加到listeners中,
  subscribe(listener) {
    this.listeners.push(listener)
    return () => {//返回的这个函数,我再把报警器给拿出来,
      let index = this.listeners.indexOf(listener)
      this.listeners.splice(index, 1)
    }
  }
  //这个函数 就是用来控制修改state的开启按钮
  dispatch = (action) => {
    //拿到了外面要修改仓库数据的纸条(action) 这个纸条上可能会带着一些数据,
    //作为开发者在使用的时候我们其实还是要再把reducer写好,这样reducer与action才能配套使用
    //光有action(纸条)么有reducer帮你修改仓库 是没用的 光有reducer,没用纸条告诉你要修改什么,你也不能乱搞仓库
    //修改完了之后把仓库更新一波
    this.state = this.reducer(action)
    //这个地方 调用了你之前添加的报警器 告诉外面的人 你纸条上的任务 已经被完成了
    //这个地方我又把state给返回出去了,自己写这些代码就是可以为所欲为,
    this.listeners.forEach(listener => listener(this.getState()))
  }
  //这个方法就是在constructor中调用的方法,它的作用就是把state中的部分分配给指定的reducer,
  combineReducers(reducers) {//把reducer组合起来
      //reducers={
  //   person1:person1Reducer,
  //   person2:person2Reducer
  // }
    const reducerAry = Object.entries(reducers)
    // reducerAry=[
    //   [reducerKey,reducer],
    //   [reducerKey,reducer],
    // ]
    return (action) => {//返回的这个函数将在 dispatch方法中的第一行执行
      //创建一个新的state最后让这个state替换换来仓库中自己管理的那部分state
      const nextState = {}
      // 遍历一波传递进来的reducers
      reducerAry.forEach(([reducerKey, reducer]) => {
        // 下面这行代码执行 会将仓库中与reducer对应的state区域 给reducer,还有小纸条action,
        //小纸条被执行完后,把最新的state给nextState,
        //再细致一点就是
        // action1=>reducer1=>state1=>nextState=>this.state
        // action2=>reducer2=>state2=>nextState=>this.state
        // action被派发之后进过自己的那个reducer产生自己管理区域的state放到nextState中,最后替换this.state
        nextState[reducerKey] = reducer(this.getState()[reducerKey], action)
      })
      return nextState
    }
  }
  //下面就是最恶心的地方了,你要让自己的dispatch能够处理多种小纸条,
  //并且还要能够让开发者自己添加一些中间件,丰富dispatch的功能
  //这个地方先看看redux-thunk的源码和砖家的文章
  // const thunkMiddleware = store => next => action => {
  //   if (typeof action === 'function') {
  //     action(store.dispatch, store.getState())
  //   } else {
  //     next(action)
  //   }
  // }
  // const loggerMiddleware = store => next => (action) => {
  //   console.log(store.getState())
  //   next(action)
  //   console.log(store.getState())
  // }
  //我上面写了2 个middleware当然是瞄的源码哈哈哈
  //大体上知道了中间件的具体形式,store参数肯定就是我这的实例了,action肯定就是小纸条了,这个next就头疼了
  //我是这么想的,这个地方中间件都是在增强dispatch的能力,反正不管怎么说,最初始的dispatch你还是要执行的
  //但是在执行最初始的dispatch的时候,先要看这个action是否能被thunk和logger处理,他们都处理完了,那么我执行最初始的dispatch
  //所以最初始的dispatch你需要保留下来,然后重写this.dispatch

  useMiddleware(middlewares) {//thunk(logger(this.dispatch))
    //第一步我先把每个middleware都注入实例
    middlewares = middlewares.map(item => item(this))
    // 下面就是改造this.dispatch, 改造之后的middleware都是返回的这样的一个函数next=>action=>{}
    // 我用例子来说明下中间件们是怎么嵌套的 调用形式就变成 thunk(logger(this.dispatch))
    //注意这里一下的的thunk和logger都是已经注入过实例的返回函数,
    //其实是先看logger里面先传入了this.dispatch, 返回了 action=>{} 这个函数 this.dispatch 不就是形参next吗,你品,你细品
    //回头看下logger里面的代码有个next(action)  翻译翻译 就是this.diapatch(action)
    //再看外层 thunk(logger(this.dispatch)) thunk把logger执行一次返回的函数作为了参数,
    // 也就是说thunk里面的next就是logger里的action=>{} 函数 所以thunk内的next(action)就是执行了logger中间件
    //logger里面的next就是执行的最初始的this.dispatch了,
    //那么下面这个代码主要是让开发者使用的更舒服的代码,中间件有很多要是像我们以上的使用方式,估计要嵌套疯了
    //拿到上面的中间件数组,然后让他们变成thunk(logger(this.dispatch))这种形式就行了,
    //正好用了下reduce,让初始值为最出的this.dispatch,执行顺序为
    // middlewares=[logger,thunk,mengxin]
    // a=this.dispatch
    // b=logger
    // a = logger(this.dispatch)
    // b=thunk
    // a = thunk(logger(this.dispatch))
    // b = mengxin//mengx是我随便写的一个中间件名字
    // a=mengxin(thunk(logger(this.dispatch)))//最后返回
    this.dispatch = middlewares.reduce((a, b) => b(a), this.dispatch)
    // 打完收工
  }
}

使用一波

function person1Reducer(state, action) {
  if (action.type === 'plus1') {
    return { ...state, age: state.age + action.payload }
  } else if (action.type === 'minus1') {
    return { ...state, age: --state.age }
  }
  return state
}
function person2Reducer(state, action) {
  if (action.type === 'plus2') {
    return { ...state, age: state.age + action.payload }
  } else if (action.type === 'minus2') {
    return { ...state, age: --state.age }
  }
  return state
}
const loggerMiddleware = store => next => (action) => {
  console.log(store.getState())
  next(action)
  console.log(store.getState())
}
const thunkMiddleware = store => next => action => {
  if (typeof action === 'function') {
    action(store.dispatch, store.getState())
  } else {
    next(action)
  }
}

let store = new Store({
  person1: person1Reducer,
  person2: person2Reducer,
}, {
  person1: { name: 'xiaoming', age: 20 },
  person2: { name: 'xiaoming', age: 20 }
})

let unSubscribt = store.subscribe((state) => {
  console.log(state.person1.age, state.person2.age)
})

let loginApi = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(100)//模拟ajax获取数据
    }, 2000)
  })
};

const login = async (dispatch) => {
  let res = await loginApi()
  console.log(res)
  return dispatch({ type: 'plus1', payload: res })

}
// let thunk = thunkMiddleware(store)
// let logger = loggerMiddleware(store)
// let middleware = thunk(logger(store.dispatch))
// store.useMiddleware(middleware)
store.useMiddleware([thunkMiddleware])
store.dispatch(login)
// store.useMiddleware(thunkMiddleware(loggerMiddleware(store.dispatch)))
// store.dispatch(thunkMiddleware(loggerMiddleware(store))(action))
// store.dispatch({ type: 'plus1', payload: 5 })

最后

以上只是我参考着实现的一部分功能,要是能帮助你,那最好不过,
最后再次感谢砖家的鼎力支持,想要更清晰的了解,还是看他的文章最好
https://github.com/brickspert...

拜拜ヾ( ̄▽ ̄)Bye~Bye~

03-05 15:27