Skip to content

状态内监视

除了通过store.watch来监听状态外,还可以在状态内部声明watch函数来监听状态数据的变化。

工作原理

AutoStore提供了watch函数,用来在state中声明一个监听对象,然后监听函数的返回值写入声明所在路径。

watch函数的基本特性如下:

  • 在状态中的任意位置,使用watch(...)来声明一个监听器对象。
  • createStore执行后,会扫描状态数据,如果发现一个值是watch(...),则会创建一个WatchObject对象,用来监听State中的数据变化。
  • 创建的WatchObject对象会保存在Store对象的watchObjects对象中
  • 当所监听的数据发生变化时,会调用WatchObject对象的getter函数,然后将返回结果写入到声明watch(...)的位置。

基本用法

watch函数签名如下:

ts
// 监听filter过滤后的
function watch<Value = any, DependValue = any>(
    getter: WatchGetter<Value, DependValue>,
    filter?: WatchDependFilter<DependValue>,
    options?: Omit<WatchOptions<Value>, "filter">,
): WatchDescriptorBuilder<Value>;
// 监听全部
function watch<Value = any, DependValue = any>(
    getter: WatchGetter<Value, DependValue>,
    options?: Omit<WatchOptions<Value>, "filter">,
): WatchDescriptorBuilder<Value>;

watch函数基本使用如下:

loading

在以上例子中,我们使用watch实现了computed的类似功能:

  • watch函数的第一个参数是getter函数,负责在依赖变化时计算新值。getter函数的返回值会写入watch函数所在的位置。
  • watch函数的第二个参数是一个过滤函数,当状态变化时会调用此方法,如果返回true才会执行getter
  • initial属性用来配置watch函数所在位置的total的初始值。

监听函数

watchgetter参数只能是一个同步函数,签名如下:

typescript
type WatchGetter<Value = any, DependValue = any> = (
    // 传入发生变化的路径和值
    scope: { path: string[]; value: DependValue },
    // 创建的watchObject对象
    watchObject: WatchObject<Value>,
) => Exclude<any, Promise<any>> | undefined;

监听参数

watch支持以下参数

ts
interface WatchOptions<Value = any> extends ObserverOptions<Value> {
    async?: false;
    filter: WatchDependFilter<Value>;
}
  • initial: 用来指定一个默认值
  • id: 用来指定一个唯一的标识,同时作为创建的WatchObjectkey,可以通过store.watchObjects.get(<id>)来访问。

动态依赖

computed计算函数的依赖一般是确定的,而watch函数的依赖可以是动态的。这比较适合一些需要动态侦听的场景。

比如上例中,我们动态侦听orders[].count的变化来计算total。而computed函数的依赖是静态的,一旦声明就不会变化。

以下是表单validate检测的简单示例:

tsx
const store = new AutoStore({
    a: {
        validate: true,
    },
    b: {
        validate: true,
    },
    c: {
        validate: true,
        c1: {
            validate: true,
        },
    },
    validate: watch<boolean, boolean>(
        ({ path, value }, watchObj) => {
            if (typeof value === "boolean") {
                const srcKey = path.join(".");
                if (value) {
                    delete watchObj.cache[srcKey];
                } else {
                    watchObj.cache[srcKey] = value;
                }
            }
            // 由于cache里面只记录validate=false的值,所以如果cache不为空则代表有字段的validate=false
            return Object.keys(watchObj.cache).length == 0;
        },
        (path) => path[path.length - 1] == "validate", // 只侦听validate字段的值变化
        { initial: true, id: "x" },
    ),
});

说明:

  • 上例中,我们需要实现一个validate字段来表单整个表单的有效,当状态中任意一个对象中的validate字段都为false时,则validate=false,否则为true
  • 现在问题是validate可能是在一个复杂的嵌套对象中,并且可能是动态的。这时候,我们无法使用computed来进行计算,因为computed的依赖是静态的。
  • 此时就是使用watch函数的时候了,我们声明一个watch函数,用来监听所有路径中的path[path.length-1]=='validate'字段的变化即可。
  • 关于WatchObject的介绍,可以参考监听对象

提示

watch会监听整个状态对象的写操作