louzhedong / blog

前端基础,深入以及算法数据结构
934 stars 84 forks source link

实现一个简单的Redux #234

Open louzhedong opened 3 years ago

louzhedong commented 3 years ago

实现一个简单的Redux

状态管理是当前前端框架开发当中一个很重要的点

当业务或者系统复杂时,状态管理能让我们更加清晰地了解数据的流动

本文通过实现一个简单的Redux来阐述一个状态管理库的思想

什么是状态管理,我们为什么需要状态管理

目前前端开发框架盛行,在框架的使用过程中就会遇到一个问题:组件间的数据通信如何来做。简单是父子组件我们可以通过props来实现数据通信,当组件层级变深,仅靠props就不足够支撑我们的需求,一个全局的状态管理也就变为一个迫切的需求。

Redux的思想

  1. Store

    Redux只有一个Store,Store不能直接进行修改,每次操作之后都是返回一个新的对象

  2. Action

    Action就是View发出的通知,告诉Store 数据要改变,Action通过特定的type来知晓做何种操作

    const action = {
     type: 'plus',
     data: {}
    };
  3. Reducer

    Reducer是一个纯函数,接受action和state,然后返回一个新的state。

总结一下,Redux的流程如下:

// View 发出 Action
store.dispatch(action);

// store 自动调用 Reducer,并且传入两个参数:当前 State 和收到的 Action。 Reducer 会返回新的 State
let nextState = xxxReducer(previousState, action);

// State 一旦有变化,Store 就会调用监听函数
store.subscribe(listener);

// listener中可以修改组件的状态,重新渲染组件
function listerner() {
  let newState = store.getState();
  component.setState(newState);   
}

实现一个简单的Redux

状态管理,说到底就是将需要共享的数据存放在一个公共的地方,所有组件都通过它来进行数据的操作。自然而然的,我们可以想到创建一个全局的对象来存放数据,所以先创建一个state.js文件

// state.js
export const state = {
    count: 0,
}

有了数据,我们就需要有数据的存取,以及发布订阅事件

因此有了以下的store.js文件

// store.js
import { state } from './state.js';
export const createStore = (reducer) => {
  let currentState = state;  // state

  function getState() {  // 获取state
    return currentState;
  }

  function dispatch() {  // 设置state
  }

  function subscribe() {  // 订阅事件
  }

  return { getState, dispatch, subscribe };
}

在这里其实用到了闭包的思想

其中getState很简单,就是获取当前的state

dispatch

状态管理库的一个原则是不能随便修改状态的值,需要做到有条理,有标记地修改。我们将操作单独封装在reducer.js中,以此来保持store.js的纯净

// reducer.js
import { state as initialState } from './state.js';
export function reducer(state = initialState, action) {
  switch(action.type) {
    case 'plus':
      return {
        ...state,
        count: state.count + 1
      };
      break;
    case 'subtract':
      return {
                ...state,
        count: state.count - 1
      }
    break;
    default:
      return initialState;
  }
}

然后在store.js中进行使用

// store.js
import { state } from './state.js';
export const createStore = (reducer) => {
  let currentState = state;  // state

  function getState() {  // 获取state
    return currentState;
  }

  function dispatch(action) {  // 设置state
    currentState = reducer(currentState, action);
  }

  function subscribe() {  // 订阅事件
  }

  return { getState, dispatch, subscribe };
}

状态管理库的另一个能力就是能够通过数据改变引起视图的改变。我们通过发布订阅模式,就能实现这一效果,通过subscribe()来订阅事件,在dispatch()的时候触发订阅的消息。当然真正的状态管理库中这一块肯定是很复杂的,我们这里只是简单地写个示例。

我们通过一个数组来存放监听的事件,通过subscribe来添加事件

// store.js
import { state } from './state.js';
export const createStore = (reducer) => {
  let currentState = state;  // state
    let queue = [];

  function getState() {  // 获取state
    return currentState;
  }

  function dispatch(action) {  // 设置state
    currentState = reducer(currentState, action);
    queue.forEach(fn => {
      fn();
    })
  }

  function subscribe(fn) {  // 订阅事件
    queue.push(fn);
  }

  return { getState, dispatch, subscribe };
}

之后添加测试用例来进行测试

import 'createStore' from './store';

const store = createStore(reducer);
store.subscribe(() => { console.log('component 1') });
store.subscribe(() => { console.log('component 2') });
store.dispatch({ type: 'plus' });
store.dispatch({ type: 'plus' });
console.log(store.getState());

// component 1
// component 2
// component 1
// component 2
// { count: 2 }

这就是一个简单的Redux的思想。