libin1991 / libin_Blog

爬虫-博客大全
https://libin.netlify.com/
124 stars 17 forks source link

让我们来做一个简单的redux原理解析吧(vuex类似) #611

Open libin1991 opened 6 years ago

libin1991 commented 6 years ago

redux状态管理器,实质上就是一个单例模式。我们来实现一个简单的redux模型,实现之前我们要先熟悉它的用法。

  • Redux是将整个应用状态存储到到一个地方,称为store里面保存一棵状态树(state tree)
  • 组件可以派发(dispatch)行为(action)给store,而不是直接通知其它组件
  • 其它组件可以通过订阅store中的状态(state)来刷新自己的视图.

下面我们按照这个思想来想想怎么做。 我们来抽象一下,提取出最核心的思想,动用鬼斧神工画个图:

用文字来描述一下,一个唯一的仓库里,有一个私有属性state,仓库由门卫大哥进行管理,所有对state的操作都要经过门卫大哥,外面的人无权直接对state进行操作,如果有进行订阅,则在状态改变后收到状态改变事件。好了,我们按照这个思想来开始code吧

第一步:声明一个对象

const state={
    a: 1
}

第二步:将对象包裹起来,使其不可随意访问

function createStore(){
    const state={
        a: 1
    }
}

第三步:暴露出一个方法,使外部可以对状态进行操作

function createStore() {
     let state={
        a:1
      };
    function getState() {
        return state //此处直接将state返回,会使state引用地址暴露,从而被引用对象改变值
    }
    return {
        getState
    }
}
let store = createStore()  // 创建一个仓库
let state = store.getState()  // 获取状态state
console.log(store.getState())  //  输出{ a: 1 }
state.a = 2   //将state a的值设置为2
console.log(state)  //  输出为{ a: 2 }
console.log(store.getState()) // 此时仓库中state的值也改变了,输出为{ a: 2 }

所以我们将第六行 return state 替换为 return JSON.parse(JSON.stringify(state)) 可以避免这个问题。

第四步:除了获取状态我们还需要操作状态,暴露第二个方法,dispatch,顺便将state的初始化进行一下优化

'use strict'
function createStore() {
  let state;
 function getState() {
   return JSON.parse(JSON.stringify(state))
 }
 function dispatch(action) { // 分发
   state = reducer(state,action) // 接收当前 State 和Action作为参数,返回一个新的 State
 }
 dispatch({ type: '@@INIT' }) // 在创建仓库的时候,初始化state的值
 return {
   getState,
   dispatch
 }
}
let initState = {
  count: 0
}
//处理器,接收二个参数 ,接收老状态和action,返回新状态 
function reducer(state = initState, action) { // 如果state没有值,默认值为initState
   //判断动作的类型
   switch (action.type) {
       case 'ADD_TODO': 
           return { ...state, count: action.number }; //...state解构state所有属性,count: action.number覆盖前面的值
       default:
           return state;
   }
}
let store = createStore()  // 创建一个仓库
let state = store.getState()  // 获取状态state
let action = {
 type: 'ADD_TODO',
 number: 1
};
store.dispatch(action);  // 派发一个action,改变state的状态
console.log(store.getState()) // 输出{ count: 1 }

diapatch中执行我们定义的reducer处理器函数,增删改查。例子演示了先创建一个仓库,在创建新仓库的时候初始化了state。然后diapatch一个action:ADD_TODO,执行的处理是改变count的值,返回一个新的state对象,最后我们可以看到输出,原来的state在初始化后变成{ count: 0 },又在ADD_TODO后变成了{ count: 1 }。

reducer函数是我们在使用redux时需要自己定义的处理函数。

至此,我们已经实现了创建一个仓库,并且可以自定义一些处理函数对state进行操作。还缺了什么呢?在实际项目中,状态改变后我们的大部分的组件需要立即得到新的状态,然后根据状态改变作出不同的处理。也就是说组件对state进行一个监听,一旦state发生改变,立马通知到对应的组件。让我们来继续实现吧。。。

第五步:增加一个订阅功能subscribe


function createStore() {
  let state;
  let listeners = [];

 function getState() {
   return JSON.parse(JSON.stringify(state))
 }

 function dispatch(action) { // 分发
   state = reducer(state,action); // 接收当前 State 和Action作为参数,返回一个新的 State
   listeners.forEach(listener => listener()) // 一旦状态改变,触发所有的监听函数,这里需要优化,只有相关状态改变才需要触发
 }

 function subscribe(listener){ // 订阅,如果需监听状态变化,将监听函数传过来
    listeners.push(listener); // 保存监听函数到监听数组
    return function () { // 返回取消订阅的函数
      listeners = listeners.filter(item => item != listener); // 过滤监听函数
    }
 }

 dispatch({ type: '@@INIT' }); // 在创建仓库的时候,初始化state的值

 return {
   getState,
   dispatch,
   subscribe
 }

}

/*这里是分割线,上面一部分是仓库定义,下面部分是使用方法*/
let initState = {
  count: 0
}
//处理器,接收二个参数 ,接收老状态和action,返回新状态 
function reducer(state = initState, action) { // 如果state没有值,默认值为initState
   //判断动作的类型
   switch (action.type) {
       case 'ADD_TODO': 
           return { ...state, count: action.number }; //...state解构state所有属性,count: action.number覆盖前面的值
       default:
           return state;
   }
}
let store = createStore()  // 创建一个仓库

let action = { // 定义一个action
 type: 'ADD_TODO',
 number: 1
};

let unADD = store.subscribe(function(){ // 监听状态改变
  console.log('状态改变了,现在的state为:')  // 状态改变了,现在的state为:
  console.log(store.getState()) // { count: 1 }
})

store.dispatch(action);  // 派发一个action,改变state的状态
复制代码

铛铛铛~,我们的redux基本模型就做好了,有什么不懂的可以提问哟~