(鸿蒙)StateStore全局状态管理源码剖析

1.概述

StateStore它是用于ArkUI状态与UI解耦的解决方案,支持全局状态维护,多个组件可以共享以及更新全局状态,将状态管理逻辑从UI组件中分离出来,简化代码结构,提高代码的可维护性。

StateStore开源文档

2.功能特性

1.状态与UI解耦,支持状态全局化操作。

目前现状:在开发复杂应用时,状态与UI的强耦合常常导致代码臃肿、难以维护,尤其是在多个组件需要共享状态时,此时需要组件之间进行层层的传递,难以维护。
StateStore:它将我们的状态管理逻辑从UI组件中抽离出来,实现集中式管理,多个组件可以共享同一个状态对象,而无需通过层层传递数据的方式来实现状态同步,从而大大降低了组件之间的耦合度。

2.支持在子线程中进行状态对象更新。

目前现状:应用开发中,子线程是无法直接修改或操作UI状态,所以子线程在处理完成数据后需要将数据发送给主线程,主线程中来进行UI状态的更新。
StateStore:它内部帮我们封装了状态同步逻辑,我们无需关心,只需要将数据发送给主线程即可,这里也采用了线程间通信对象Sendable协议,它默认线程间对象的传递是通过引用传递,在结合特性1的状态与UI解耦。

3.支持状态更新执行的预处理和后处理。

目前现状:应用开发中想要在状态更新前后插入一些自定义的逻辑,我们需要将代码便在对应的状态更新代码业务逻辑前后,这样会造成代码臃肿,难以维护。
StateStore:它提供了一个一套中间件(Middleware)可以方便的在状态更新前以及后插入自定义的逻辑,并且是解耦。

3.StateStore中核心角色

  • StateStore(全局的状态管理实例对象):它是一个共享模块,进程只能加载一次,且不同线程间共享,提供一些初始化等方法。
  • Store(全局状态管理仓库):主要维护两个关键方法:getState 和 dispatch(框架的核心方法)
  • Reducer(状态处理函数):真正处理状态更新的函数
  • Action(事件描述对象):用于描述事件类型,以及事件携带的具体数据。
  • Middleware(中间件):提供预处理/后处理函数。

4.源码解析

1.StateStore的共享模块
// 声明当前模块为共享模块,只能导出可Sendable数据
// "use shared" 指令标记一个模块为共享模块
//  注意:共享模块,进程内只会加载一次的模块,且不同线程间共享,StateStore是全局实例对象唯一。
'use shared'

@Sendable
class InnerStateStore {

  /**
   * 创建状态管理仓库Store。
   * @param storeId       仓库ID,每一个仓库的ID是唯一的
   * @param state         状态管理数据类  @Observed /  @ObservedV2 + @Trace
   * @param actions       自定义类型别名Reducer,就是一个函数 (state: T, action: Action) => (() => Promise<void>) | null;
   * @param middlewares   状态更新前后的 预处理/后处理实现
   * @returns
   */
  public createStore<T>(
    storeId: string,
    state: T,
    actions: Reducer<T>,
    middlewares?: Middleware<T>[]
  ): Store<T> {
    /**
     * 创建一个全局的仓库Store,Store是状态管理的核心,主要向外部提供两个方法
     *    getState()  获取当前的状态信息
     *    dispatch    处理来自 UI分发的Action事件
     */
    const store = createStore(state, actions, middlewares);
    stores.set(storeId, store);
    return store;
  }

  ...

  /**
   * 创建一个事件描述对象
   * @param type      标识事件的类型(描述一个事件要做什么),根据类型来处理对应的数据的更新。
   * @param payload   事件处理时携带的具体数据
   * @returns
   */
  public createAction(type: string, payload: ESObject = null):Action {
    ...
    return new Action(type, payload);
  }

  /**
   * 子线程创建一个Sendable格式的Action对象。
   */
  public createSendableAction(storeId: string, type: string, payload: lang.ISendable | null = null): SendableAction {
    ....
    const sendableAction = new SendableAction(storeId, type, payload);
    return sendableAction;
  }

  /**
   * 主线程接收一个SendableAction对象并触发reducer函数。
   * @param sendableAction
   */
  public receiveSendableAction<T>(sendableAction: SendableAction) {
    const storeId = sendableAction.storeId;

    //判断是否存在key为storeId,如果不存在则返回。
    if (!stores.hasKey(storeId)) {
      StoreLogger.error(StoreError.STORE_NOT_FOUND, 'receiveSendableAction');
      return;
    }
    ...
    
    const action: Action = new Action(
      sendableAction.type,
      sendableAction.payload
    )
    const store = this.getStore<T>(storeId);
    //帮我们调用dispatch来触发UI的更新。
    store.dispatch(action);
  }

  /**
   * 将观察对象的多个reducer合并为单个reducer
   * @param reducers
   * @returns
   */
  public combineReducers<T>(
    reducers: Record<string, Reducer<ESObject>>
  ): (state: T, action: Action) => null {

    return combineReducers<T>(reducers);
  }
}

export let StateStore = new InnerStateStore();
2.StateStore的核心方法createStore解析
  • 该方法主要就是实现两个函数。getState 和 dispatch
    • getState:获取当前状态对象,该方法实现简单,直接return传入的状态对象。
    • dispatch:核心方法,该方法整体步骤:
      • 1.初始化中间件,校验以及过滤(过滤符合条件的中间件)
      • 2.执行中间件:beforeActions(如果存在)
      • 3.执行reducer,真正状态更新的函数
      • 4.执行中间件:afterActions(如果存在)
export function createStore<T>(
  initialState: T,
  reducer: Reducer<T>,
  middlewares?: Middleware<T>[]
): Store<T> {

  let totalMiddleware = middlewares || [];
  //当前的Reducer
  let currentReducer = reducer;

  //1.getState方法实现简单,只是返回了当前状态对象
  const getState = (): T => {
    return initialState;
  }

  //2.dispatch核心
  const dispatch = async (action: Action): Promise<void> => {
    // 用户传入的 action对象
    let currentAction = action;
    // 中间件:beforeAction数组
    let beforeActions: MiddlewareFuncType<T>[] = [];
    // 中间件:afterAction数组
    let afterActions: MiddlewareFuncType<T>[] = [];

    // 用于校验: 用户传入的动作,确保动作对象的‘ type ’属性存在。
    if (currentAction.type === '' && currentAction.type.trim() === '') {
      StoreLogger.error(StoreError.INVALID_ACTION_TYPE, 'dispatch');
      throw Error('分派失败,类型属性不能为空字符串。');
    }

    //---------------------- 中间件-> 校验以及过滤 ---------------------
    //定义一个局部变量,用于保存符合条件的中间件。
    let currentMiddleware: Middleware<T>[] = [];

    //是否存在中间件(前后的预处理能力)
    if (totalMiddleware.length) {
      //过滤符合条件的中间件,并返回新数组
      currentMiddleware = totalMiddleware.filter((middleware: Middleware<T>) => {
        return middleware.actionType === '' ||
          (middleware.actionType !== '' && middleware.actionType === action.type);
      });

      // 分别存储中间件beforeAction和afterAction
      if (currentMiddleware && currentMiddleware.length > 0) {
        currentMiddleware.forEach((middleware: Middleware<T>) => {
          // 向数组末尾添加一个元素
          beforeActions.push(middleware.beforeAction);
          // 向数组开头添加一个元素
          afterActions.unshift(middleware.afterAction);
        })
      }
    }
    //---------------------- 中间件-> 校验以及过滤 ---------------------


    // 将中间件修改的操作类型存储在reducer中。
    const resetAction: Action[] = [];
    try {
      //---------------------- 中间件->预处理操作 ---------------------
      // 执行中间件 beforeAction 的逻辑
      try {
        if (beforeActions.length > 0) {
          const shouldContinue = await executeMiddleware<T>(initialState, currentAction, beforeActions, resetAction);
          //如果返回false
          if (!shouldContinue) {
            return; //函数会直接退出,中断代码,下方reducer将不会执行。
          }
        }
      } catch (e) {
        StoreLogger.error('Catch errors during the execution of the beforeAction in the middleware %s',
            `message is ${e.message}`);
        return;
      }

      // 如果中间件beforeAction,返回一个新的动作(Action),修改reducer将要执行的动作类型。(相当于篡改了用户的动作Action)
      if (resetAction.length === 1) {
        currentAction = resetAction[0];
      }
      //---------------------- 中间件->预处理操作 ---------------------


      /**
       * 触发reducer
       *  参数1:状态对象
       *  参数2:action对象,type用于描述事件动作
       */
      const result = currentReducer(initialState, currentAction);



      //---------------------- 中间件->后处理操作 ---------------------
      // reducer返回一个函数来执行异步操作。  相当于在reducer中  return async() => {}
      if (typeof result === 'function') {
        let func: AsyncFunction = result as AsyncFunction;
        //执行函数成功后,开始执行中间件的afterAction后处理操作。
        func().then(async () => {
          try {
            if (afterActions.length > 0) {
              const shouldContinue = await executeMiddleware<T>(initialState, currentAction, afterActions, resetAction);
              if (!shouldContinue) {
                return;
              }
            }
          } catch (err) {
            StoreLogger.error('Catch errors during the execution of the afterAction in the middleware %s',
              `message is ${err.message}`);
          }
        })
      } else {
        //没有返回函数的情况,依然需要处理,中间件的afterAction后处理操作
        if (afterActions.length > 0) {
          const shouldContinue = await executeMiddleware<T>(initialState, currentAction, afterActions, resetAction);
          if (!shouldContinue) {
            return;
          }
        }
      }
      //---------------------- 中间件->后处理操作 ---------------------
    } catch (outerErr) {
      StoreLogger.error('Catch errors during the execution of the dispatch in the middleware %s',
          `message is ${outerErr.message}`);
    }
    return;
  }

  return {
    getState,
    dispatch,
  }
}

export interface Store<T> {
  /**
   * 获取观察状态对象
   */
  getState: () => T;

  /**
   * 派发一个动作。这是触发状态变化的方式。
   */
  dispatch: Dispatch;
}


//自定义的函数类型别名
export type Dispatch = (action: Action) => Promise<void>;

//自定义函数类型别名,接收两个参数(state: T, action: Action) 并返回 无参数Promise函数 或者 null
export type Reducer<T> = (state: T, action: Action) => (() => Promise<void>) | null;

//事件描述对象
export class Action {
  readonly type: string;        //事件类型
  payload: ESObject;            //事件携带具体的数据
  ...
  setPayload(payload: ESObject) {
    this.payload = payload
    return this;
  }
}

/**
 * 中间件
 *   beforeAction: 中间件前置处理,返回值为联合函数,MiddlewareStatus | Action,  
 *   afterAction: 中间件后置处理,返回值为联合函数,MiddlewareStatus | Action,
 */
export abstract class Middleware<T> {
  /**
   * 如果定义了actionType,中间件将只在以下情况下执行
   *   对应调度的Action的type相同情况会执行。
   */
  actionType: string = '';

  abstract beforeAction: MiddlewareFuncType<T>;

  abstract afterAction: MiddlewareFuncType<T>;
}

/**
 * 定义的类型别名MiddlewareFuncType
 */
export type MiddlewareFuncType<T> = (
  (state: T, action: Action) => MiddlewareStatus | Action
) | (
  (state: T, action: Action) => Promise<MiddlewareStatus | Action >
)

/**
 * 中间件的状态
 */
export enum MiddlewareStatus {
  DROP, // Interrupt
  NEXT, // continue
}
  • StateStore没有中间件时执行流程图


    image.png
  • StateStore整体的执行流程图


    image.png
3.StateStore的中间件的处理解析
  • 这里需要注意中间件的返回值
    • MiddlewareStatus.DROP:函数会直接退出,循环也随之停止执行。
    • MiddlewareStatus.NEXT:继续执行。
    • Action:相当于篡改用户的Action行为。
/**
 * 中间件中beforeAction和afterAction函数的执行逻辑
 */
export const executeMiddleware = async <T>(
  state: T,
  action: Action,
  subscribers: MiddlewareFuncType<T>[],
  resetAction: Action[]
): Promise<boolean> => {
  //用户传入的action对象
  let currentAction = action;

  //遍历当前Store中所有的中间件的beforeAction集合
  for (const sub of subscribers) {
    // 重置在中间件中传递的操作。
    if (resetAction.length === 1) currentAction = resetAction[0];
    //执行beforeAction
    const result: MiddlewareStatus | Action = await sub(state, currentAction);

    if (result === MiddlewareStatus.DROP) {
      /**
       * 1. 如果前一个动作函数返回 MiddlewareStatus.DROP,函数会直接退出,循环也随之停止执行。
       */
      return false;
    } else if (isAction(result)) {
      /**
       * 2.如果前一个中间件函数返回一个动作对象,后续中间件函数和reducer将执行返回的动作对象。
       *  相当于篡改了我们用户传入的Action对象。
       */
      resetAction[0] = result as Action;
    }
  }
  // 3. 如果前面的函数返回 MiddlewareStatus.NEXT,继续执行。
  return true;
}
4.StateStore的支持在子线程中进行状态对象更新解析
  • 这里采用的是Sendable协议,上面也有介绍,可以进行查看。
  • 核心主要是StateStore的receiveSendableAction方法,帮我们封装好了触发状态更新的函数。
  public receiveSendableAction<T>(sendableAction: SendableAction) {
    const storeId = sendableAction.storeId;
    ...
    //1.将SendableAction换为Action
    const action: Action = new Action(
      sendableAction.type,
      sendableAction.payload
    )
    //2.根据storeId获取对应的Store对象
    const store = this.getStore<T>(storeId);
    //3.触发Store的dispatch,从而触发状态的更新。
    store.dispatch(action);
  }

5.StateStore与传统状态管理对比

特性 传统状态管理(@State,@Prop等) State(全局状态管理)
生命周期 父子组件内 全局应用层级
状态共享 组件之间传递 全局的Store仓库,获取状态对象
状态修改 组件内对状态装饰器的变量赋值 组件内调用Store的dispatch方法发送一个事件对象
维护性 低(状态分散在各个组件内) 高(Reducer函数集中管理)
使用场景 单组件/父子组件状态共享 1.跨组件状态共享 2.支持中间并且解耦 3.简化子线程同步状态

6.FAQ

官方文档

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容