AutoStore API
AutoStore 是 AutoStore 的核心类,提供基于 Proxy 的响应式状态管理。
构造函数
constructor
constructor(state?: State, options?: AutoStoreOptions<State>)创建一个 AutoStore 实例。
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
state | State | 初始状态对象 |
options | AutoStoreOptions<State> | 配置选项 |
配置选项:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
id | string | 自动生成 | Store 实例标识 |
debug | boolean | false | 是否启用调试模式 |
lazy | boolean | false | 是否延迟创建计算对象 |
shadow | boolean | false | 是否声明为影子 Store |
enableComputed | boolean | true | 是否启用计算属性(全局开关) |
enableValueExpr | boolean | true | 是否启用字符串表达式解析 |
reentry | boolean | true | 计算函数是否允许重入 |
resetable | boolean | string[] | false | 是否启用重置功能,或限定可重置的路径规则 |
cascadeDestroy | boolean | true | 依赖删除时是否级联销毁 |
plugins | IAutoStorePlugin[] | [] | 功能插件列表 |
logger | ILogger | - | 自定义日志器 |
scope | ComputedScope | - | 默认的计算作用域 |
getRootScope | function | - | 获取计算函数根作用域 |
configManager | ConfigManager | false | - | 配置管理器 |
configKey | string | store.id | 配置项前缀 |
onInvalid | string | - | 校验失败时的默认行为 |
validate | function | - | 全局校验函数 |
validators | Record<string, function> | - | 路径级别的校验函数 |
refStore | AutoStore | AutoStore[] | - | 引用其他 Store |
sandbox | object | - | 代码执行沙箱配置 |
示例:
import { AutoStore } from "autostore";
const store = new AutoStore(
{
user: {
name: "张三",
age: 25,
},
order: {
price: 100,
count: 2,
},
},
{
id: "my-store",
debug: true,
resetable: true,
}
);属性
state
get state(): ComputedState<State>获取响应式状态对象。通过 Proxy 代理,读写操作会自动触发事件。
示例:
// 读取状态
console.log(store.state.user.name);
// 修改状态(触发事件)
store.state.user.name = "李四";id
get id(): string获取 Store 实例的唯一标识符。
console.log(store.id); // "my-store" 或自动生成的 IDoptions
get options(): AutoStoreOptions<State>获取 Store 的配置选项。
operates
get operates(): FastLiteEvent<StateChangeEvents>获取状态操作事件触发器。用于订阅状态变化事件。
// 订阅特定路径的变化
store.operates.on("user.name", (operate) => {
console.log("user.name 变化:", operate);
});
// 订阅所有变化
store.operates.onAny((operate) => {
console.log("状态变化:", operate);
});computedObjects
get computedObjects(): ComputedObjects<State>获取计算对象管理器,用于管理所有计算属性实例。
watchObjects
get watchObjects(): WatchObjects<State>获取监听对象管理器,用于管理所有 watch 监听器实例。
errors
get errors(): Record<string, string>获取校验错误信息集合。键为路径,值为错误信息。
silenting
get silenting(): boolean获取当前是否处于静默更新状态。
batching
get batching(): boolean获取当前是否处于批量更新状态。
peeping
get peeping(): boolean获取当前是否处于偷看模式(读取状态但不触发事件)。
delimiter
get delimiter(): string获取路径分隔符,默认为 "/"。
logger
get logger(): ILogger获取日志器实例。
configManager
get configManager(): ConfigManager获取配置管理器实例。
configKey
get configKey(): string获取当前 Store 的配置键前缀。
resetable
get resetable(): boolean | string[]
set resetable(value: boolean | string[])获取或设置重置范围。启用后,路径首次变化前的基线会被记录,可通过 reset() 还原。默认值为 false(需显式开启)。详见重置状态。
// 显式开启
const store = new AutoStore(state, { resetable: true });
// 或
store.resetable = true;
// 修改状态
store.state.user.name = "李四";
// 重置到基线
store.reset();取值
| 值 | 含义 |
|---|---|
true | 全部路径可重置(零匹配开销的快路径) |
false / [] | 禁用,不捕获任何基线 |
string[] | 按路径规则限定重置范围 |
路径规则
规则首匹配即止——按书写顺序依次匹配,命中即决定该路径是否可重置,不再检查后续规则。
// 纳入规则(无前缀):命中即判定该路径可重置,范围为整棵子树(含自身)
resetable: ["orders.items"];
// 排除规则(`!` 前缀):命中即判定该路径不可重置
resetable: ["!orders.items"];
// 顺序重要:更靠前的规则遮蔽更靠后的规则
resetable: ["!orders.items", "orders"];
// orders.items → 命中规则 1 → 不可重置
// orders.price → 规则 1 不命中 → 规则 2 命中 → 可重置
// 反过来写,排除规则就成了死代码(orders 整棵子树都先被规则 1 命中)
resetable: ["orders", "!orders.items"];
// orders.items → 命中规则 1 → 可重置通配符:* 匹配恰好一层,** 匹配任意层(含零层,故 ["!**"] 排除整棵树)。
零命中兜底:存在纳入规则时默认不可重置(规则集是收窄);只有排除规则时默认可重置(["!x"] 即"除 x 外都记")。
规则在捕获时判定一次,并在
reset()时复算一次——捕获之后修改规则,已捕获的基线也按新规则处理。
行为约束
- 容器基线会吸收其后代:
state.user = {...}只产生一条基线,其后对state.user.x的修改不再单独记录。 - 若某容器在其子树内已有脏路径之后才被整体替换,则不捕获该容器基线(否则会吞掉后代的还原),此时还原降级为逐个脏路径。
- 计算属性的路径不捕获基线。
- 每次赋值
store.resetable = ...都会清空已有基线——适用集已变,旧基线不再可靠。 store.resetable返回规则数组的副本,外部改动不会影响已生效的规则。
resetableExplicit
get resetableExplicit(): booleanresetable 是否由用户显式配置。只读,无 setter。
与 resetable 读数的区别:resetable 永远是「生效值」——构造器已把默认值合并进 options,未配置时读出 false;本属性回答的是「这个值是用户给的,还是落到默认值的」。
new AutoStore(state).resetableExplicit; // false,落到默认值
new AutoStore(state, { resetable: false }).resetableExplicit; // true,显式拒绝
new AutoStore(state, { resetable: true }).resetableExplicit; // true
new AutoStore(state, { resetable: ["a"] }).resetableExplicit; // true构造参数与 setter 赋值都会将其置为 true:
const store = new AutoStore(state);
store.resetableExplicit; // false
store.resetable = false; // 之后
store.resetableExplicit; // true典型用法是让消费方在未配置时补开启,同时尊重用户显式的 false。AutoForm 绑定外部 store 时即依此处理:form.reset() 依赖基线捕获,若外部 store 落到默认值会让 reset() 静默失效,因此 form 会自动开启;但用户显式传了 resetable: false 表示拒绝,form 不覆盖。
补开启的时机
基线自开启那一刻起算。若 store 在被 form 接管之前已被修改,那部分变更不在可还原范围内。需要完整回滚时,请在首次修改前开启。
plugins
get plugins(): IAutoStorePlugin[]获取已安装的插件列表。
updatedState
get updatedState(): Record<string, any>已捕获基线的只读调试视图:键为路径,值为该路径首次变化前的基线值(数组为元素表副本)。
该视图仅为兼容旧调试代码而保留,不是还原依据。键在基线时刻不存在时值为
undefined。
方法
watch
watch(listener: WatchListener, options?: WatchListenerOptions): Watcher
watch(paths: StatePath | StatePath[], listener: WatchListener, options?: WatchListenerOptions): Watcher监听状态变化。
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
paths | string | string[] | 要监听的路径,支持通配符 * 和 ** |
listener | function | 状态变化时的回调函数 |
options | WatchListenerOptions | 配置选项 |
options 选项:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
once | boolean | false | 是否只监听一次 |
operates | string | string[] | "write" | 过滤操作类型 |
filter | function | - | 自定义过滤器 |
depth | number | 0 | 监听深度(0/1/≥2) |
返回值: Watcher 对象,提供 off() 方法取消监听。
// 监听所有变化
const watcher1 = store.watch((operate) => {
console.log(operate);
});
// 监听指定路径
const watcher2 = store.watch("user.name", ({ value, oldValue }) => {
console.log(`姓名变化: ${oldValue} -> ${value}`);
});
// 监听多个路径
const watcher3 = store.watch(
["user.name", "order.price"],
(operate) => {
console.log(operate.path.join("."), "变化");
}
);
// 使用通配符
store.watch("user.*", (operate) => {
console.log("user 下的属性变化:", operate);
});
// 配置选项
store.watch("order", callback, {
once: true,
operates: "write",
depth: 2,
});
// 取消监听
watcher1.off();update
update(fn: (state: ComputedState<State>) => void, options?: UpdateOptions): void在函数内部更新状态值。
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
fn | function | 更新函数,在函数内修改 state |
options | UpdateOptions | 配置选项 |
options 选项:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
batch | boolean | string | false | 是否批量更新 |
silent | boolean | false | 是否静默更新(不触发事件) |
peep | boolean | false | 是否偷看模式 |
reply | boolean | true | 批量更新后是否回放操作事件 |
flags | number | 0 | 额外的更新标识 |
onInvalid | string | - | 校验失败时的行为 |
// 普通更新
store.update((state) => {
state.user.name = "李四";
state.user.age = 26;
});
// 批量更新(更新完成后触发一次 batch 事件)
store.update(
(state) => {
state.user.name = "李四";
state.user.age = 26;
},
{ batch: true }
);
// 静默更新(不触发任何事件)
store.update(
(state) => {
state.user.name = "李四";
},
{ silent: true }
);
// 指定批量更新事件名称
store.update(
(state) => {
state.user.name = "李四";
},
{ batch: "user-update" }
);silentUpdate
silentUpdate(fn: (state: ComputedState<State>) => void): void静默更新状态,不触发任何事件。等价于 update(fn, { silent: true })。
store.silentUpdate((state) => {
state.user.name = "李四";
});batchUpdate
batchUpdate(fn: (state: ComputedState<State>) => void): void批量更新状态,更新完成后触发一次 __batch_update__ 事件。等价于 update(fn, { batch: true, onInvalid: "pass" })。
store.batchUpdate((state) => {
state.user.name = "李四";
state.user.age = 26;
state.order.count = 5;
});peep
peep<Value>(getter: (state: State) => Value): Value
peep<PATH>(path: PATH): GetTypeByPath<ComputedState<State>, PATH>
peep<Value>(path: string[]): Value偷看状态值,读取过程中不触发 get 事件。
// 使用路径字符串
const name = store.peep("user.name");
// 使用路径数组
const name2 = store.peep(["user", "name"]);
// 使用 getter 函数
const total = store.peep((state) => state.order.price * state.order.count);collectDependencies
collectDependencies(fn: () => void, operates?: WatchListenerOptions["operates"]): string[][]执行函数并收集函数内部读写操作涉及的依赖路径。
// 收集所有操作的依赖
const deps = store.collectDependencies(() => {
store.state.user.name;
store.state.order.price;
});
// deps = [["user", "name"], ["order", "price"]]
// 只收集读操作的依赖
const readDeps = store.collectDependencies(() => {
store.state.user.name;
store.state.user.age = 26; // 写操作不收集
}, "read");
// readDeps = [["user", "name"]]getSnap
getSnap<Entry>(options?: { entry?: Entry; reserveAsync?: boolean; includeFunc?: boolean }): GetTypeByPath<ComputedState<State>, Entry>返回状态的快照数据(纯对象,非 Proxy)。
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
entry | string | - | 指定路径,不传则返回整个状态 |
reserveAsync | boolean | true | 是否保留异步对象结构 |
includeFunc | boolean | - | 是否包含函数属性 |
// 获取整个状态快照
const snapshot = store.getSnap();
// 获取指定路径的快照
const userSnap = store.getSnap({ entry: "user" });
// 不保留异步对象结构
const snap = store.getSnap({ reserveAsync: false });get
get<T>(path: T, options?: { defaultValue?: any; waitAsyncDone?: boolean; timeout?: number }): any获取指定路径的值,支持异步计算属性等待。
| 选项 | 类型 | 说明 |
|---|---|---|
defaultValue | any | 路径不存在时的默认值 |
waitAsyncDone | boolean | 是否等待异步计算完成 |
timeout | number | 等待异步计算的超时时间(毫秒) |
// 同步获取
const name = store.get("user.name");
// 获取不存在的路径,返回默认值
const val = store.get("nonexistent", { defaultValue: "默认值" });
// 等待异步计算完成
const asyncVal = await store.get("user.asyncData", {
waitAsyncDone: true,
timeout: 5000,
});reset
reset(entry?: string): void将状态还原到基线——即路径首次变化前的快照,而非逆放变更操作。需要先启用 resetable(默认为 false)。
// 先启用
store.resetable = true;
// 修改状态
store.state.user.name = "李四";
store.state.order.price = 200;
// 重置所有状态
store.reset();
// 重置指定路径及其后代
store.reset("user");语义要点
- 还原的是基线,不是逆放操作:
list.push(4)后再pop(),reset()回到的是变脏前的[1,2,3],而非空数组。 entry精确匹配该路径及其后代,不会误伤同前缀的兄弟路径(reset("user")不影响username)。未命中任何基线时静默无操作。- 数组的
push/pop/splice/shift/unshift/fill/length/单下标赋值/delete下标均可精确还原。 - 新增的键还原为消失,删除的键还原为恢复;键原本存在但值为
undefined时还原为undefined。 - 键名含
.时可正确还原(内部按转义路径编码,不产生多余层级)。 - 还原一个容器只产生一次节点级写回,其后代不单独还原。
- 永不抛出异常:逐条
try/catch,失败项告警且保留其基线以便重试;仅清除成功还原的条目。 - 计算属性的路径不参与还原——它们在依赖变化时自动重算。
- 已被判定为不可重置的路径不会被还原;容器的整体还原也不会顺带还原它们。
已知限制
@autostorejs/plugins/watch 与 @autostorejs/plugins/shadow 在 reset() 期间不会刷新——它们的 onAny 订阅收不到广播派生事件。设计取舍见仓库 docs/adr/0037-reset-baseline-model.md。
destroy
destroy(): void销毁 Store 实例,取消所有事件订阅和监听器。
store.destroy();createObserverObject
createObserverObject(descriptor: AnyObserverDescriptor, context?: ObserverContext): ObserverObject | undefined创建观察者对象实例(内部方法,通常不直接调用)。
toString
toString(): string返回 Store 的字符串表示。
console.log(store.toString()); // "AutoStore<my-store>"