同步
AutoStore
内置同步机制,可以方便的实现两个AutoStore
之间的数据同步。
使用方法
完全同步
将store1
的state
同步到store2
的state
中。
const store1 = new AutoStore({
order: {
name: 'fisher',
price: 2,
count: 3,
total: computed((order) => order.price * order.count),
},
});
const store2 = new AutoStore<typeof store1.state.order>();
store1.sync(store2);
提示
同步是双向的。
部分同步
将store1.state.order
同步到store2.state.myorder
中。
const store1 = new AutoStore({
order: {
name: 'fisher',
price: 2,
count: 3,
total: computed((order) => order.price * order.count),
},
});
const store2 = new AutoStore<typeof store1.state.order>({
myorder: {},
});
store1.sync(store2, { from: 'order', to: 'myorder' });
指南
同步配置
sync
方法可以接受一个配置对象,配置对象可以有以下属性:
属性 | 类型 | 说明 |
---|---|---|
from | string | 当前store 的state 的路径 |
to | string | 目标store 的state 的路径 |
direction | 'both' | 'forward' | 'backward' | 同步方向,both =双向,forward =前向同步,backward =后向同步 |
filter | (operate:StateOperate)=>boolean | 过滤函数,返回true 表示同步,返回false 表示不同步 |
immediate | boolean | 是否立即同步,默认为true ,即初始化时马上执行一次同步 |
同步方向
默认情况下,同步是双向的。
const store1 = new AutoStore({...})
const store2 = new AutoStore({...})
// 默认双向同步
store1.sync(store2)
// 复制store1的变更到store2
store1.sync(store2,{direction:'forward'})
// 复制store2的变更到store1
store1.sync(store2,{direction:'backward'})
局部同步
通过form
和to
属性同步状态的局部,而不是全同步。
const store1 = new AutoStore({...})
const store2 = new AutoStore({...})
// 完全同步
store1.sync(store2)
// 将store1.state.order同步到store2.state
store1.sync(store2,{from:"order"})
// 将store1.state.order同步到store2.state.myorder
store1.sync(store2,{from:"order",to:"myorder"})
// 将store1.state同步到store2.state.myorder
store1.sync(store2,{to:"myorder"})
过滤同步
默认情况下,会对from
路径下的所有数据进行同步,可以额外通过filter
函数过滤。
const store1 = new AutoStore({
order:{
a:1,
b:2,
c:3,
d:4
}
})
const store2 = new AutoStore({...})
// 完全同步,
store1.sync(store2,{
filter:(operate)=>operate.path[0]==='order'
})
filter
可以返回true
表示同步,返回false
表示不同步。operate
对象是StateOperate
类型,包含了更新信息,类型如下:
export type StateOperate<Value = any, Parent = any> = {
type: StateOperateType;
path: string[];
value: Value;
indexs?: number[];
oldValue?: Value;
parentPath?: string[];
parent?: Parent;
reply?: boolean;
flags?: number | symbol;
};
克隆同步
AutoStore
提供一个Clone
方法,可以克隆一个store
,克隆的store
与原store
是相互独立的,但是state
是同步的。
// 完全克隆,但不同步
const store1 = new AutoStore({...})
const store2 = store1.clone({...<AutoStore Options>...})
// 完全克隆,但不同步
const store2 = store1.clone()
const store2 = store1.clone({sync:'none'})
// 完全克隆,但双向同步
const store2 = store1.clone({sync:'both'})
// 完全克隆,前向同步
const store2 = store1.clone({sync:'farward'})
// 完全克隆,前向同步
const store2 = store1.clone({sync:'backward'})
// 克隆store1.state.<path>,前向同步
const store2 = store1.clone({
entry:"<path>",
sync:'backward'
})
- 执行
clone
方法后,默认不会同步。
打开/关闭同步
同步可以通过on
和off
方法来打开和关闭同步。
const store1 = new AutoStore({...})
const store2 = new AutoStore({...})
const syncer = store1.sync(store2)
// 关闭同步
syncer.off()
// 打开同步
syncer.on()
路径映射
允许通过pathMap
来控制两个AutoStore
之间的路径映射关系。
const fromStore = new AutoStore({
order: {
a: 1,
b: 2,
c: 3,
},
});
const toStore = new AutoStore({
myorder: {},
});
fromStore.sync(toStore, {
to: 'myorder',
immediate: false,
pathMap: {
to: (path: string[]) => {
return [path.join('.')];
},
from: (path: string[]) => {
return path.reduce<string[]>((result, cur) => {
result.push(...cur.split('.'));
return result;
}, []);
},
},
});
以上代码:
to
参数表示将fromStore
中的order.a
映射到toStore
中的myorder[order.a]
。from
参数表示将toStore
中的myorder[order.a]
映射到fromStore
中的order.a
。
所以同步后生效后:
fromStore.state.order.a = 11;
fromStore.state.order.b = 12;
fromStore.state.order.c = 13;
expect(toStore.state).toEqual({
myorder: {
'order.a': 11,
'order.b': 12,
'order.c': 13,
},
});
反之,如果修改toStore
中的myorder[order.a]
,fromStore
中的order.a
也会同步更新。
toStore.state.myorder['order.a'] = 21;
toStore.state.myorder['order.b'] = 22;
toStore.state.myorder['order.c'] = 23;
expect(fromStore.state).toEqual({
order: {
a: 21,
b: 22,
c: 23,
},
});
注意
以上配置了immediate=false
,所以不会立即进行全同步,而是进行增量同步,仅当fromStore
变化时才会同步变更项到toStore
中。
如果我们指定immediate=true
,则代表需要在初始化进行一次全同步。
以上的代码需要进行修改:
fromStore.sync(toStore, {
to: 'myorder',
pathMap: {
to: (path: string[], value: any) => {
if (typeof value !== 'object') {
return [path.join('.')];
}
},
from: (path: string[], value: any) => {
if (typeof value !== 'object') {
return path.reduce<string[]>((result, cur) => {
result.push(...cur.split('.'));
return result;
}, []);
}
},
},
});
重点:
当immediate=true
时,我们无法在进行第一次同步时使用Object.assign
来快速同步,而只能使用for...in
来深度遍历fromStore
中的所有属性(包括后代对象),然后逐条进行更新。
如果路径映射代码是:
pathMap: {
to: (path: string[], value: any) => {
return [path.join('.')];
};
}
则会在toStore
中写入:
{
myorder:{
'order':{a:1,b:2,c:3}
'order.a':1,
'order.b':2,
'order.c':3
}
}
多了一个order
属性,为了避免这种情况,要点在于,当value
是基本类型时,才进行路径转换,如果值类型是object
,则需要返回undefined
。
因此代码需要修改为:
fromStore.sync(toStore, {
to: 'myorder',
pathMap: {
to: (path: string[], value: any) => {
if (typeof value !== 'object') {
return [path.join('.')];
}
},
from: (path: string[], value: any) => {
if (typeof value !== 'object') {
return path.reduce<string[]>((result, cur) => {
result.push(...cur.split('.'));
return result;
}, []);
}
},
},
});
- 当遍历
fromStore
时,如果遇到object
类型时,则返回undefined
,这样toStore
中就不会写入多余的order
属性。
以下完整示例:
远程同步
远程同步可以实现将本地的AutoStore
与远程的AutoStore
进行同步。比如将浏览器的AutoStore
与WebWorker
的AutoStore
进行同步。
远程同步需要通过安装@autostore/syncer
来实现。
使用方法
以下是@autostore/syncer
的简单使用示例,实现worker
与browser
之间的同步。
- 第 1 步:开发一个
Transport
用来进行传输更新操作,实现IAutoStoreSyncTransport
接口。
// transport.ts
export class WorkerTransport implements IAutoStoreSyncTransport {
private _callback: (operate: StateRemoteOperate) => void;
ready = true;
constructor(public worker) {
this.worker.addEventListener('message', this.onReceive.bind(this));
}
send(operate: StateRemoteOperate) {
this.worker.postMessage(operate);
}
receive(callback: (operate: StateRemoteOperate) => void): void {
this._callback = callback;
}
onReceive(data: StateRemoteOperate) {
this._callback(data);
}
}
需要实现IAutoStoreSyncTransport
接口,接口定义如下:
export interface IAutoStoreSyncTransport {
// 如果为false,表示当前不可用,更新操作会被暂时写入缓存
ready: boolean;
// 发送更新操作
send(operate: StateRemoteOperate): void;
// 接收更新操作
receive(callback: (operate: StateRemoteOperate) => void): void;
}
- 第 2 步:配置本地
AutoStore
这里本地AutoStore
需要配置syncer
,并指定transport
。
// browser.ts
import {AutoStore} from '@autostore/core'
import { AutoStoreSyncer } from '@autostore/syncer'
const worker = new Worker('./worker.ts')
const browserStore = new AutoStore({...})
const syncer = new AutoStoreSyncer(browserStore,{
transport: new WorkerTransport(worker),
autostart: true
})
- 第 3 步:远程
AutoStore
// worker.ts
import { AutoStore } from '@autostore/core'
import { AutoStoreSyncer } from '@autostore/syncer'
import { WorkerTransport } from './transport'
const workerStore = new AutoStore({...})
const syncer = new AutoStoreSyncer(workerStore,{
transport: new WorkerTransport(self),
autostart: true
})
控制同步操作
开始和停止同步。
const syncer = new AutoStoreSyncer(workerStore, {
transport: new WorkerTransport(self),
autostart: true,
});
// 开始同步
syncer.start();
// 停止同步
syncer.stop();
同步缓存
默认情况下,当transport
不可用时,即transport.ready=false
,更新操作会被暂时写入缓存。
当transport
可用时,应该调用syncer.flush()
,将缓存中的更新操作发送到远程AutoStore
。
const syncer = new AutoStoreSyncer(workerStore, {
transport: new WorkerTransport(self),
});
syncer.flush();
- 注意,
transport
是否可用,是由Transport
自己决定。当可用状态切换时,应当调用syncer.flush()
。 - 当
transport
不可用时,更新操作会被暂时写入缓存,缓存大小由maxCacheSize
控制,默认为100
。
同步映射
支持通过entry
和remoteEntry
来控制两个AutoStore
之间的同步映射关系。
new AutoStoreSyncer(localStore, {
transport: localTransport,
entry: ['order'],
remoteEntry: ['remoteOrder'],
});
以上代表要将localStore
中的order
映射到remoteStore
中的remoteOrder
。
同步参数
参数 | 类型 | 默认值 | 说明 |
---|---|---|---|
autostart | boolean | true | 是否自动开始同步 |
maxCacheSize | number | 100 | 缓存大小 |
entry | string[] | [] | 同步映射关系 |
remoteEntry | string[] | [] | 同步映射关系 |
immediate | boolean | false | 是否立即同步 |
onSend | (operate:StateRemoteOperate) => boolean | 发送更新操作回调,返回false 表示不发送 | |
onReceive | (operate:StateRemoteOperate) => boolean | 接收更新操作回调,返回false 表示接收 |
type StateRemoteOperate {
type : StateOperateType,
path : string[],
value : Value,
indexs : number[],
flags : number
}
手动全同步
调用syncer.update()
方法可以向远程store
要求一次全同步。
const remoteStore = new AutoStore({
x: {
order: {
price: 100,
count: 2,
total: computed((order) => order.price * order.count),
},
},
});
const localStore = new AutoStore({
y: {},
});
const remoteSyncer = new AutoStoreSyncer(remoteStore, { transport: remoteTransport });
const localSyncer = new AutoStoreSyncer(localStore, { transport: localTransport, entry: ['y'], remoteEntry: ['x'] });
expect(localStore.state.y).toEqual({});
localSyncer.update();
expect(localStore.state.y).toEqual(remoteStore.state.x);
- 配置
options.immediate
为true
会在启动时自动调用syncer.update()
。
停止同步
可以通过.stop
方法停止同步。
const syncer = new AutoStoreSyncer(localStore, {
transport: localTransport,
});
syncer.stop();
当调用stop()
后,syncer
会停止同步,然后:
- 调用
transport.onStop
方法,因此可以在停止同步时进行断开连接等工作。 - 同时会向对方发送一个
stop
操作指令,对方的syncer
会收到这个指令,也会停止同步和调用transport.onStop
方法。
示例
以下是同步时进行路径映射的示例: