美文网首页
从零开始撸一个Redux

从零开始撸一个Redux

作者: zdxhxh | 来源:发表于2020-08-16 22:18 被阅读0次

Redux

Redux 属于视图状态管理工具,它解决了传统 MVVM 架构的 VM 层状态管理责任过重的问题,他使用订阅发布的模式,将 VM 层的状态集中式的管理,这样做的好处有

  • 视图通讯变得简单,多个视图可以轻松共用状态
  • 代码分割更加简便

它有以下特点

  • 不支持异步,派发 action 的过程中,没有实现thunk接口,不能实现阻塞
  • 支持对 state 的 cbs(控制反转) 风格处理,reducer 为函数

开始撸一个 redux

我们建立一个redux文件夹,然后在该目录创建一个index.js

import createStore from "./core/createStore";
export default createStore;

接下来我们来实现createStore方法,在该目录下建立core目录,并在该目录下建立createStore.js文件,创建createStore方法

createStore,创建一个 store,一个 redux 架构应该只有一个 store,该 store 用于维持 state,派发 action,注册监听器,

redux 使用的是函数式编程,所以下面会用有一个闭包函数,执行后返回 redux 提供的 API

export default function createStore(reducer, preLoadState, enhancer) {
  if (enhancer) {
    // 中间件处理
    return enhancer(createStore)(reducer, preLoadState);
  }

  function getState() {} // 获取state

  function subscribe() {}

  function dispatch() {}

  function replaceReducer() {}
  // 告诉控制台,初始化store 顺便触发reducer的default分支,拿到initialState
  dispatch({ type: "REDUX_INIT" });
  return {
    getState,
    subscribe,
    dispatch,
    replaceReducer,
  };
}

A. getState (获取状态)

getState 用于获取 store 的 state,接下来我们来实现 getState 方法

export default function createStore(reducer, preLoadState, enhancer) {
  let currentState = preLoadState;
  function getState() {
    if (isDispatching) {
      throw new Error("你不能获取state,因为现在正在派发action");
    }
    return currentState;
  }
  // ...
}

B. dispatch (调度)

dispatch 方法用于派发一个 action,action 由typepayload组成

export default function createStore(reducer, preLoadState, enhancer) {
  let isDispatching = false;
  let currentReducer = reducer;
  let currentListeners = [];
  let nextListeners = currentListeners;

  function dispatch(action) {
    try {
      isDispatching = true;
      // 处理传入的action
      currentState = currentReducer(currentState, action);
    } finally {
      isDispatching = false;
    }
    currentListeners = nextListeners;
    const listeners = currentListeners;
    // 遍历订阅者,通知action已经派发完成
    listeners.forEach((lisntener) => listener());
    return action;
  }
  // ...
}

C. subscribe (订阅)

该方法用于提供给组件订阅 store 的变化,当 store 发生变化时,既可在订阅方法中更新视图

export default function createStore(reducer, preLoadState, enhancer) {
  let isSubscribed = false;
  let nextListeners = [];
  function subscribe(listener) {
    nextListeners.push(listener);
    return function unsubscribe() {
      if (!isSubscribed) {
        return;
      }
      if (isDispatching) {
        throw new Error("不能在派发action的时候,取消store的订阅");
      }
      isSubscribed = true;
      // 可以直接通过indexOf进行函数的查找
      const index = nextListeners.indexOf(listener);
      // 删除缓存中的订阅函数
      nextListeners.splice(index, 1);
    };
  }
  // ...
}

D. replaceReducer (替换处理者)

用于替换 store 的 reducer 对象,这个方法没什么特别的

export default function createStore(reducer, preLoadState, enhancer) {
  function replaceReducer(nextReducer) {
    currentReducer = nextReducer;
    // 告诉控制台,替换了reducer
    dispatch({ type: "REDUX_REPLACE" });
  }
  // ...
}

结束

这样,我们就简单的撸完一个 redux 了,我们以 React 为例子,来看看如何使用它

// reducer
const reducer = (state = {}, action) => {
  switch (action.tpye) {
    case "TMD": {
      // 对state进行处理
      state.name = "干啥啥不行";
      return state;
    }
    default: {
      return state;
    }
  }
};

const store = createStore(reducer);

import React from "react";

class App extends React.Component {
  constructor() {
    super();
    this.state = store.getState();
  }

  componentDidMount() {
    this.unscribe = store.subscribe(() => {
      this.setState(store.getState());
    });
  }

  render() {
    const styleObj = {
      background: "#66ccff",
      width: "200px",
      color: "#fff",
      textAlign: "center",
      cursor: "pointer",
    };
    return (
      <ul>
        以下是model的数据 : <br />
        <div style={styleObj} onClick={this.changeStore.bind(this)}>
          点击这里改变store数据
        </div>
        {JSON.stringify(this.state.model)}
      </ul>
    );
  }
}

相关文章

网友评论

      本文标题:从零开始撸一个Redux

      本文链接:https://www.haomeiwen.com/subject/haixjktx.html