前言
很久之前看过@砖家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~