鸿蒙极速入门(六)-加载请求状态管理-LoadState+观察者模式

背景

1、在ArkTS的架构中,没有明确的可管理的加载请求状态的脚手架,在进行网络请求过程中,无法简单的进行交互响应。

2、参考Android中的LoadState写了一个简单的脚手架,以便在日常开发过程中,管理加载请求状态和UI交互。

脚手架说明与源码

1、状态机LoadState

使用一个状态机,分别对应网络请求过程中的Loading(发起请求)、Loaded(请求成功)、LoadError(请求失败)状态,并支持链式调用:

/**
 * 网络请求MVVM数据模型,由子类实现状态机管理,由方法实现回调监听
 */
export abstract class LoadState {
  /**
   * loading函数,如果当前状态是Loading,则调用回调函数
   * @param callBack 回调函数
   * @returns this
   */
  loading(callBack?: () => void): this {
    if (this instanceof Loading) {
      callBack?.call(null);
    }
    return this;
  }

  /**
   * loaded函数,如果当前状态是Loaded,则调用回调函数
   * @param callBack 回调函数
   * @returns this
   */
  loaded(callBack?: (result: Loaded<any>) => void): this {
    if (this instanceof Loaded) {
      callBack?.call(null, this);
    }
    return this;
  }

  /**
   * loadError函数,如果当前状态是LoadError,则调用回调函数
   * @param callBack 回调函数
   * @returns this
   */
  loadError(callBack?: (error: LoadError) => void): this {
    if (this instanceof LoadError) {
      callBack?.call(null, this);
    }
    return this;
  }
}

/**
 * Loading类,继承自LoadState类
 */
export class Loading extends LoadState {}

/**
 * Loaded类,继承自LoadState类,包含一个result属性和一个data方法
 */
export class Loaded<T> extends LoadState {
  result?: T;

  constructor(data: T) {
    super();
    this.result = data;
  }

  data(): T | undefined {
    return this.result;
  }
}

/**
 * LoadError类,继承自LoadState类,包含code和message属性
 */
export class LoadError extends LoadState {
  code?: number;
  message?: string;

  constructor(code: number, message: string) {
    super();
    this.code = code;
    this.message = message;
  }
}

2、观察者模式

ArtTS没有提供开箱即用的观察者模式框架,也无法直接使用RxJS框架,所以自己手写一个简单的ValueNotifier作为观察者实现类:

/**
 * ValueNotifier类,包含_value、listeners属性和addListener、notifyListeners、value方法
 */
export class ValueNotifier<T> {
  private _value: T;
  listeners: Array<() => void> = [];

  constructor(value: T) {
    this._value = value;
  }

  get value(): T {
    return this._value;
  }

  set value(value: T) {
    this._value = value;
    this.notifyListeners();
  }

  addListener(listener: () => void) {
    this.listeners.push(listener);
  }

  notifyListeners() {
    for (let listener of this.listeners) {
      listener();
    }
  }
}

使用示例

以获取一个车辆详情的场景来模拟网络请求和数据处理

1、ViewModel

import { Loaded, LoadError, Loading, LoadState, ValueNotifier } from './LoadState';

export class VehicleViewModel {
  lsVehicleDetail: ValueNotifier<LoadState | null>;

  constructor() {
    this.lsVehicleDetail = new ValueNotifier<LoadState | null>(null);
  }

  // 获取车辆详情
  async getVehicleDetail() {
    // 发起请求
    this.lsVehicleDetail.value = new Loading();

    await new Promise(resolve => setTimeout(resolve, 3000));

    // 获得数据
    this.lsVehicleDetail.value = new Loaded("aa");

    await new Promise(resolve => setTimeout(resolve, 3000));

    // 模拟网络报错
    this.lsVehicleDetail.value = new LoadError(123, "error");
  }
}

2、页面处理

@Component
export struct VehicleComponent {
  private vm: VehicleViewModel = new VehicleViewModel();

  aboutToAppear() {

    this.vm.lsVehicleDetail.addListener(() => {
      this.vm.lsVehicleDetail.value?.loading(() => {
        // 开始网络请求
        console.log(`hello1:start Loading`);
      }).loaded((result) => {
        let data = result?.data() as String
        console.log(`hello2:${result} - ${data}`);
      }).loadError((error) => {
        console.log(`hello3:${error?.code} - ${error?.message}`);
      });
    });
  }
}

3、日志打印结果

hello1:start Loading
hello2:[object Object] - aa
hello3:123 - error