背景
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