18-redux源码

141 阅读2分钟

redux

  • 用于在项目中统一处理公用的状态
import { createStore } from 'redux';
/**
 * 本质上是一个普通的函数
 * @param {*} state: 之前的数据 
 * @param {*} action: 描述要干嘛的对象
 */
function reducer(state = 0, action) {
  // 返回一个新的状态
  switch(action.type) {
    case "increase":
      return state + 1;
    case "decrease":
      return state - 1;
    default:
      return state;
  }
}

const store = createStore(reducer)

export default store;
import React, { Component } from 'react'
import store from '../store/index'
export default class ReduxDemo extends Component {
  unsubscribe = null;
  componentWillUnmount() {
    // 取消订阅
    this.unsubscribe();
  }
  componentDidMount() {
    // 告诉redux,一旦state更新就刷新渲染
    this.unsubscribe = store.subscribe(() => {
      this.forceUpdate();
    });
  }
  add = () => {
    store.dispatch({type: "increase"})
  }
  render() {
    return (
      <div>
        <h1>{ store.getState() }</h1>
        <button onClick={this.add}>add</button>
      </div>
    )
  }
}

基础源码

function createStore(reducer) {
  let currentState;
  let currentListeners = [];
  // 获取值
  function getState() {
    return currentState;
  }

  function dispatch(action) {
    currentState = reducer(currentState, action);
    currentListeners.forEach(listener => listener());
  }
  // 订阅后渲染组件
  function subscribe(listener) {
    currentListeners.push(listener);
    return () => {
      const index = currentListeners.indexOf(listener);
      currentListeners.splice(index, 1);
    }
  }

  // 帮用户先渲染一次
  dispatch({type: "@@@@redux-cccc"})
  return {
    getState,
    dispatch,
    subscribe
  }
}
export default {
  createStore
}
// import { createStore } from 'redux';
import { createStore } from '../myRedux/redux'
/**
 * 本质上是一个普通的函数
 * @param {*} state: 之前的数据 
 * @param {*} action: 描述要干嘛的对象
 */
function reducer(state = 0, action) {
  // 返回一个新的状态
  switch(action.type) {
    case "increase":
      return state + 1;
    case "decrease":
      return state - 1;
    default:
      return state;
  }
}

const store = createStore(reducer)

export default store;
  • 组件使用
import React, { Component } from 'react'
import store from '../store/index'
export default class ReduxDemo extends Component {
  unsubscribe = null;
  componentWillUnmount() {
    // 取消订阅
    this.unsubscribe();
  }
  componentDidMount() {
    // 告诉redux,一旦state更新就刷新渲染
    this.unsubscribe = store.subscribe(() => {
      this.forceUpdate();
    });
  }
  add = () => {
    store.dispatch({type: "increase"})
  }
  minus = () => {
    store.dispatch({type: "decrease"})
  }
  render() {
    return (
      <div>
        <h1>{ store.getState() }</h1>
        <button onClick={this.add}>add</button>
        <button onClick={this.minus}>minus</button>
      </div>
    )
  }
}

效果如下: image.png

进一步完善版本

/**
 * 判断某个对象是一个plain-object(平面对象)
 * @param {*} obj 
 */
function isPlainObject(obj) {
  if(typeof obj !== "object") return false;
  return Object.getPrototypeOf(obj) === Object.prototype;
}

/**
 * 得到指定长度的随机字符串
 * @param {Number} length 
 */
function getRandomString(length) {
  let RandNum = Math.random();
  return RandNum.toString(36).slice(2, length).split("").join(".");
}


export function createStore(reducer, defaultState) {
  let currentReducer = reducer,
      currentState = defaultState,
      currentListeners = [];
  // 获取值
  function getState() {
    return currentState;
  }

  function dispatch(action) {
    // 验证action
    if(!isPlainObject(action)) {
      throw new Error("action must a plain object");
    }
    // 验证action的type属性是否存在
    if (action.type === undefined) {
      throw new Error('action must has a prop');
    }
    currentState = currentReducer(currentState, action);

    currentListeners.forEach(listener => listener());
  }
  // 订阅后渲染组件
  function subscribe(listener) {
    currentListeners.push(listener);
    let isRemove = false;//是否已经移除
    // 解除监听
    return () => {
      if (isRemove) return;
      // 将之前添加的listener从数组中移除
      const index = currentListeners.indexOf(listener);
      currentListeners.splice(index, 1);
      isRemove = true;
    }
  }

  // 创建仓库时,帮用户先初始化一次
  dispatch({type: `@@redux/Init${getRandomString(7)}`});
  return {
    getState,
    dispatch,
    subscribe
  }
}

处理中间件的

/**
 * 判断某个对象是一个plain-object(平面对象)
 * @param {*} obj 
 */
function isPlainObject(obj) {
  if(typeof obj !== "object") return false;
  return Object.getPrototypeOf(obj) === Object.prototype;
}

/**
 * 得到指定长度的随机字符串
 * @param {Number} length 
 */
function getRandomString(length) {
  let RandNum = Math.random();
  return RandNum.toString(36).slice(2, length).split("").join(".");
}


export function createStore(reducer, enhancer) {
  // 加强器 -> 加强了dispatch
  if (enhancer) {
    return enhancer(createStore)(reducer);
  } 
  let currentReducer = reducer,
      currentState,
      currentListeners = [];
  // 获取值
  function getState() {
    return currentState;
  }

  function dispatch(action) {
    // 验证action
    if(!isPlainObject(action)) {
      throw new Error("action must a plain object");
    }
    // 验证action的type属性是否存在
    if (action.type === undefined) {
      throw new Error('action must has a prop');
    }
    currentState = currentReducer(currentState, action);
    console.log(currentState);

    currentListeners.forEach(listener => listener());
  }
  // 订阅后渲染组件
  function subscribe(listener) {
    currentListeners.push(listener);
    let isRemove = false;//是否已经移除
    // 解除监听
    return () => {
      if (isRemove) return;
      // 将之前添加的listener从数组中移除
      const index = currentListeners.indexOf(listener);
      currentListeners.splice(index, 1);
      isRemove = true;
    }
  }

  // 创建仓库时,帮用户先初始化一次
  dispatch({type: `@@Redux/Init${getRandomString(7)}`});
  return {
    getState,
    dispatch,
    subscribe
  }
}

export function applyMiddleware(...middlewares) {
  // 给我创建仓库的函数
  return (createStore) => (reducer) => {
    const store = createStore(reducer);
    let dispatch = store.dispatch;

    // todo 加强dispath
    const midAPI = {
      getState: store.getState,
      dispatch: (action, ...args) => dispatch(action, ...args)
    }
    // 新的中间件数组
    const middlewareChain = middlewares.map(middleware => middleware(midAPI));
    // 加强版dispath -> 把所有中间函数执行后,同时还执行 store.dispatch
    dispatch = compose(...middlewareChain)(store.dispatch);

    return {
      ...store,
      // 加强版的dispatch
      dispatch
    }
  }
}

function compose(...func) {
  let len = func.length;
  if (len === 0) {
    return (func) => func;
  }
  if (len === 1) {
    return func[0];
  }
  return func.reduce(
    (a, b) => 
      (...args) => 
        a(b(...args))
  );
}

如下图:所示 image.png