Skip to content

同步

AutoStore内置同步机制,可以方便的实现两个AutoStore之间的数据同步。

使用方法

完全同步

store1state同步到store2state中。

ts
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中。

ts
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 当前storestate的路径
to string 目标storestate的路径
direction 'both' | 'forward' | 'backward' 同步方向,both=双向,forward=前向同步,backward=后向同步
filter (operate:StateOperate)=>boolean 过滤函数,返回true表示同步,返回false表示不同步
immediate boolean 是否立即同步,默认为true,即初始化时马上执行一次同步

同步方向

默认情况下,同步是双向的。

ts
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'})

局部同步

通过formto属性同步状态的局部,而不是全同步。

ts
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函数过滤。

ts
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类型,包含了更新信息,类型如下:
ts
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是同步的。

ts

// 完全克隆,但不同步
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方法后,默认不会同步。

打开/关闭同步

同步可以通过onoff方法来打开和关闭同步。

ts
const store1 = new AutoStore({...})
const store2 = new AutoStore({...})

const syncer = store1.sync(store2)

// 关闭同步
syncer.off()

// 打开同步
syncer.on()

路径映射

允许通过pathMap来控制两个AutoStore之间的路径映射关系。

ts
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

所以同步后生效后:

ts
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也会同步更新。

ts
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,则代表需要在初始化进行一次全同步。

以上的代码需要进行修改:

ts
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中的所有属性(包括后代对象),然后逐条进行更新。

如果路径映射代码是:

ts
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

因此代码需要修改为:

ts
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进行同步。比如将浏览器的AutoStoreWebWorkerAutoStore进行同步。

远程同步需要通过安装@autostore/syncer来实现。

使用方法

以下是@autostore/syncer的简单使用示例,实现workerbrowser之间的同步。

  • 第1步:开发一个Transport用来进行传输更新操作,实现IAutoStoreSyncTransport接口。
ts
// 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接口,接口定义如下:

ts
export interface IAutoStoreSyncTransport {
    // 如果为false,表示当前不可用,更新操作会被暂时写入缓存
    ready: boolean                  
    // 发送更新操作
    send(operate: StateRemoteOperate): void
    // 接收更新操作
    receive(callback: (operate: StateRemoteOperate) => void): void
}
  • 第2步:配置本地AutoStore

这里本地AutoStore需要配置syncer,并指定transport

ts
// 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
ts
// 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
})

控制同步操作

开始和停止同步。

ts
const syncer = new AutoStoreSyncer(workerStore,{
    transport: new WorkerTransport(self),
    autostart: true
})

// 开始同步
syncer.start()

// 停止同步
syncer.stop()

同步缓存

默认情况下,当transport不可用时,即transport.ready=false,更新操作会被暂时写入缓存。

transport可用时,应该调用syncer.flush(),将缓存中的更新操作发送到远程AutoStore

ts
const syncer = new AutoStoreSyncer(workerStore,{
    transport: new WorkerTransport(self)
})

syncer.flush()
  • 注意transport是否可用,是由Transport自己决定。当可用状态切换时,应当调用syncer.flush()
  • transport不可用时,更新操作会被暂时写入缓存,缓存大小由maxCacheSize控制,默认为100

同步映射

支持通过entryremoteEntry来控制两个AutoStore之间的同步映射关系。

ts
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表示接收
ts
type StateRemoteOperate {
    type      : StateOperateType,
    path      : string[],
    value     : Value,
    indexs    : number[],               
    flags     : number 
}

示例

以下是同步时进行路径映射的示例: