PersistenceV2 存进去的配置,重启之后一半字段是空的
“持久化”不等于“原样存回来”
截图整理工具的设置页有几个用户偏好:默认分类、是否开启自动打码、特征向量模型版本、上次导入的游标位置。用 PersistenceV2.connect 存进去,开发阶段一切正常,杀掉进程重进,一半字段是 undefined,甚至整个 connect 返回了 undefined。
两个独立的问题撞在一起了。connect 返回 undefined,是因为冷启动时 key 里还没有数据,必须带上默认值构造器。字段在重启后丢失,是因为 PersistenceV2 的序列化有严格的类型边界:类必须可序列化,带方法、Map、Set 的字段存不进去,复杂类型的属性信息在反序列化时可能丢失。

defaultCreator 是安全入口,@Type 是类型保留的钥匙
PersistenceV2.connect 的第三个参数 defaultCreator 在磁盘没有数据时提供默认实例,保证返回值不会是 undefined。另一个容易被忽略的点是嵌套类属性的类型标注。如果设置类里有一个 vectorConfig: VectorConfig 这样的嵌套对象,反序列化之后 vectorConfig 可能变成普通对象,丢失类方法或类型信息。@Type 装饰器就是用来标记属性原始类型的,确保复杂类型在序列化和反序列化过程中不丢失。
typescript
@ObservedV2
class VectorConfig {
@Trace modelVersion: string = 'v1.0';
@Trace dimension: number = 512;
}
@ObservedV2
class AppSettings {
@Trace defaultCategory: string = 'all';
@Trace autoMosaic: boolean = false;
@Trace importCursor: number = 0;
@Type(VectorConfig) // 反序列化时保留类型
@Trace vectorConfig: VectorConfig = new VectorConfig();
}
const settings = PersistenceV2.connect(
AppSettings,
'app_settings',
() => new AppSettings() // 磁盘无数据时走这里
);
@Type(VectorConfig) 必须和 @Trace 同时存在。@Trace 负责运行时属性变化的观测,@Type 负责序列化时类型信息的保留。两者管的是不同的链路,缺一个都会在特定场景下出问题。defaultCreator 返回的实例要带完整的默认字段,不能是一个空壳对象,否则迁移逻辑会因为字段缺失而误判。
旧版本升级不走构造函数,迁移必须显式做
defaultCreator 解决的是“磁盘上什么都没有”的场景。从旧版本升级上来是另一种情况:磁盘上有序列化过的 JSON,PersistenceV2 把它反序列化成对象,但不会走类的构造函数。新版本新增的字段,旧 JSON 里没有,拿到手就是 undefined。
迁移应该放在 connect 返回之后、UI 使用之前,集中做一次字段补全。不要依赖 @Trace 的默认值在反序列化路径上生效,那条路径不走构造函数。
typescript
function migrateSettings(s: AppSettings): void {
if (s.autoMosaic === undefined) s.autoMosaic = false;
if (s.importCursor === undefined) s.importCursor = 0;
if (!s.vectorConfig) s.vectorConfig = new VectorConfig();
}
只放可序列化的数据,业务逻辑不要塞进持久化类
PersistenceV2 的定位是“可序列化的状态快照”,不是“带行为的业务对象”。持久化类里只放基础类型和可被 @Type 标注的嵌套类,方法、Map、Set、闭包一律不要放。如果你需要缓存特征向量,向量本身用 number[] 或 ArrayBuffer 存进 relationalStore,AppSettings 里只保留一个模型版本号或游标位置。
连接之后还要检查 PersistenceV2.notifyOnError,序列化失败时不一定会抛到你的业务代码里,注册错误监听能在开发阶段把问题暴露出来。持久化的验收标准不是“写进去了”,是“杀掉进程、升级覆盖、清数据重装三种情况下,读出来的每一个字段都符合预期”。
更多推荐
所有评论(0)