有个大牛告诉我,初级程序员和高级程序员有个区别.
就是当看到一个效果一个框架一个组件,初级程序员会觉得,好牛逼,怎么用呢,api怎么调用,好复杂.但是高级程序员看到了之后第一时间会想,如果是我我要怎么实现,用了什么原理.
redux是一个状态管理的方案,是react最常用的一种管理状态的方式,有的人会觉得好复杂,搞不懂,只能照葫芦画瓢,那是没有明白里面的原理,接下来尝试一下如何写一个redux.
这个是官网上面的例子,首先我们要试着去实现redux,不需要考虑太多边界问题,那样过于复杂化,我们就先把最简单的东西实现了,明白里面的原理就行了.
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
let store = createStore(counter)
//嗯,这里看来是会返回一个对象
store.subscribe(() => console.log(store.getState()))
//store对象有subscribe方法,用于监听
//还有个getState方式是获取state数据的
//store有dispatch方法是用于改变state的
store.dispatch({ type: 'INCREMENT' })
// 1
store.dispatch({ type: 'INCREMENT' })
// 2
store.dispatch({ type: 'DECREMENT' })
// 1
就从上面的代码看来,首先是有个createStore方法创建返回了一个对象,那个对象有3个方法,分别是dispatch,getState,subscribe
const createStore = ()=>{
const store = {};
store.dispatch = () =>{};
store.getState = () =>{};
store.subscribe = () =>{};
return store
}
大概架构出来了,嗯,踏出了成功的第一步了.接下来一点点实现,先从最简单的开始getState,getState就是返回state嘛,那肯定要有state,那state应该在哪里实现呢?或许有的人会想放到store上面
const createStore = ()=>{
const store = {
state:undefined
};
store.getState = () => store.state; //简写的方式,等于返回return store.state
store.dispatch = () =>{};
store.subscribe = () =>{};
return store
}
但是其实这样非常不合理,如果store.state都能直接访问了,还要getState来干嘛?多此一举,就是不希望用户那么容易的拿到state,很容易影响单项数据流的问题
const createStore = ()=>{
const store = {};
let state = undefined;
store.getState = () => state; //return state
store.dispatch = () =>{};
store.subscribe = () =>{};
return store
}
这样子写,state就成了私有变量,外部不能直接访问它,只能通过特定的方式getState()去访问,完美!
再下来我们看看dispatch怎么弄吧,
store.dispatch({ type: 'INCREMENT' })
这个是dispatch的调用方法,再结合那个counter函数,很明显
const createStore = (counter)=>{
const store = {};
let state = undefined;
store.getState = () => state;
store.dispatch = (action) =>{
//counter接收2个参数,一个state,一个action,然后返回一个state去更新state
state = counter(state,action);
};
store.subscribe = () =>{};
return store
}
好了,还有最后一个监听,这个单词subscribe的意思是订阅,也就是当state发生变化时,就要告诉它,我们来看看这个怎么实现
store.subscribe(() => console.log(store.getState()))
这个是调用方式,传入一个函数当参数,嗯,这就是一个高阶函数,高阶函数听起来牛逼,把函数当成一个参数就是高阶函数了,同样react里面的高阶组件也是这样来,把组件当初参数传递的那个函数就是高阶组件....
从上面调用看来,传入一个函数,并且当state发生变化的时候,调用那个传入的函数
const createStore = (counter)=>{
const store = {};
let state = undefined;
//创建了一个数组,准备存放subscribe传进来的函数,为什么要准备一个数组,因为可能有很多个地方会订阅state的变化
const fnList = [];
store.getState = () => state;
store.dispatch = (action) =>{
//counter接收2个参数,一个state,一个action,然后返回一个state去更新state
state = counter(state,action);
//只要当我接收到要更改state的时候同时也把fnList里面的函数全部调用一次,不就起到了订阅的效果了
fnList.forEach(fn => {fn()});
};
store.subscribe = (fn) =>{
//这样调用subscribe,传进来的函数就会放到fnList里面
fnList.push(fn);
};
return store
}
这样三个函数都写完了,但是还漏了一点,就是当createStore调用的时候,会传个函数进来
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
例如传这个函数,state应该是初始化等于0才对,再优化下
const createStore = (counter) => {
const store = {};
let state;
//创建了一个数组,准备存放subscribe传进来的函数,为什么要准备一个数组,因为可能有很多个地方会订阅state的变化
const fnList = [];
store.getState = () => state;
store.dispatch = (action) => {
//counter接收2个参数,一个state,一个action,然后返回一个state去更新state
state = counter(state, action);
//只要当我接收到要更改state的时候同时也把fnList里面的函数全部调用一次,不就起到了订阅的效果了
fnList.forEach(fn => {
fn()
});
};
store.subscribe = (fn) => {
//这样调用subscribe,传进来的函数就会放到fnList里面
fnList.push(fn);
};
//就在调用createStore创建store首次调用传进来的函数,就可以拿到state的初始值了
state = counter(state, {type: ''});
return store
};
好了,最后试试放回到例子中
const createStore = (counter) => {
const store = {};
let state;
//创建了一个数组,准备存放subscribe传进来的函数,为什么要准备一个数组,因为可能有很多个地方会订阅state的变化
const fnList = [];
store.getState = () => state;
store.dispatch = (action) => {
//counter接收2个参数,一个state,一个action,然后返回一个state去更新state
state = counter(state, action);
//只要当我接收到要更改state的时候同时也把fnList里面的函数全部调用一次,不就起到了订阅的效果了
fnList.forEach(fn => {
fn()
});
};
store.subscribe = (fn) => {
//这样调用subscribe,传进来的函数就会放到fnList里面
fnList.push(fn);
};
//就在调用createStore创建store首次调用传进来的函数,就可以拿到state的初始值了
state = counter(state, {type: ''});
return store
};
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
let store = createStore(counter);
store.getState()
store.subscribe(() =>
console.log(store.getState())
);
store.dispatch({ type: 'INCREMENT' });
// 1
store.dispatch({ type: 'INCREMENT' });
// 2
store.dispatch({ type: 'DECREMENT' });
// 1
效果是一样的,当然redux肯定不会这么简单,还有一些其他的方法,并且很多边界问题要考虑,例如传进来的是不是函数,是什么类型,都要判断并且抛出错误提示用户,这样才是一个完整的代码.
最后,写的不好,大牛看到了不要嘲讽我,看到有啥错误可以指出来.