同步
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
}
示例
以下是同步时进行路径映射的示例: