Vue3从入门到精通: 4.5 数据持久化与同步策略深度解析

👋 大家好,我是 阿问学长!专注于分享优质开源项目解析、毕业设计项目指导支持、幼小初高教辅资料推荐等,欢迎关注交流!🚀

数据持久化与同步策略深度解析

🎯 学习目标

通过本文,你将深入掌握:

  • 多层次数据持久化策略的设计
  • 离线优先的数据同步机制
  • 实时数据同步和冲突解决方案
  • 数据版本控制和增量同步技术
  • 大规模应用的数据管理架构

💾 多层次数据持久化架构

持久化存储的分层设计

现代Web应用需要在多个层次上进行数据持久化,以提供最佳的用户体验和数据可靠性:

javascript 复制代码
// 持久化存储分层架构
/*
┌─────────────────────────────────────┐
│        Memory Layer (内存层)        │  ← 最快访问,易失性
├─────────────────────────────────────┤
│      Session Storage (会话层)       │  ← 会话级持久化
├─────────────────────────────────────┤
│       Local Storage (本地层)        │  ← 持久化存储
├─────────────────────────────────────┤
│        IndexedDB (数据库层)         │  ← 结构化大容量存储
├─────────────────────────────────────┤
│         Cache API (缓存层)          │  ← Service Worker缓存
├─────────────────────────────────────┤
│        Remote Server (服务端)       │  ← 权威数据源
└─────────────────────────────────────┘
*/

class LayeredPersistenceManager {
  constructor() {
    this.layers = new Map()
    this.setupLayers()
  }
  
  setupLayers() {
    // 内存层 - 最快访问
    this.layers.set('memory', {
      storage: new Map(),
      priority: 1,
      capacity: 1000, // 最大条目数
      ttl: 5 * 60 * 1000, // 5分钟TTL
      
      async get(key) {
        const item = this.storage.get(key)
        if (!item) return null
        
        // 检查TTL
        if (Date.now() - item.timestamp > this.ttl) {
          this.storage.delete(key)
          return null
        }
        
        return item.data
      },
      
      async set(key, data) {
        // 容量控制
        if (this.storage.size >= this.capacity) {
          this.evictOldest()
        }
        
        this.storage.set(key, {
          data,
          timestamp: Date.now()
        })
      },
      
      evictOldest() {
        const entries = Array.from(this.storage.entries())
        entries.sort((a, b) => a[1].timestamp - b[1].timestamp)
        
        // 删除最旧的25%
        const toDelete = Math.floor(entries.length * 0.25)
        for (let i = 0; i < toDelete; i++) {
          this.storage.delete(entries[i][0])
        }
      }
    })
    
    // 会话存储层
    this.layers.set('session', {
      priority: 2,
      capacity: 5 * 1024 * 1024, // 5MB
      
      async get(key) {
        try {
          const item = sessionStorage.getItem(key)
          return item ? JSON.parse(item) : null
        } catch (error) {
          console.error('Session storage get error:', error)
          return null
        }
      },
      
      async set(key, data) {
        try {
          const serialized = JSON.stringify(data)
          
          // 检查容量
          if (this.getStorageSize() + serialized.length > this.capacity) {
            this.cleanup()
          }
          
          sessionStorage.setItem(key, serialized)
        } catch (error) {
          console.error('Session storage set error:', error)
        }
      },
      
      getStorageSize() {
        let total = 0
        for (let key in sessionStorage) {
          if (sessionStorage.hasOwnProperty(key)) {
            total += sessionStorage[key].length
          }
        }
        return total
      },
      
      cleanup() {
        // 清理过期数据
        const now = Date.now()
        for (let key in sessionStorage) {
          try {
            const item = JSON.parse(sessionStorage[key])
            if (item._timestamp && now - item._timestamp > 30 * 60 * 1000) {
              sessionStorage.removeItem(key)
            }
          } catch (error) {
            // 忽略解析错误
          }
        }
      }
    })
    
    // 本地存储层
    this.layers.set('local', {
      priority: 3,
      capacity: 10 * 1024 * 1024, // 10MB
      
      async get(key) {
        try {
          const item = localStorage.getItem(key)
          if (!item) return null
          
          const parsed = JSON.parse(item)
          
          // 检查过期时间
          if (parsed._expires && Date.now() > parsed._expires) {
            localStorage.removeItem(key)
            return null
          }
          
          return parsed.data
        } catch (error) {
          console.error('Local storage get error:', error)
          return null
        }
      },
      
      async set(key, data, options = {}) {
        try {
          const item = {
            data,
            _timestamp: Date.now(),
            _expires: options.expires || null
          }
          
          localStorage.setItem(key, JSON.stringify(item))
        } catch (error) {
          if (error.name === 'QuotaExceededError') {
            this.cleanup()
            // 重试一次
            try {
              localStorage.setItem(key, JSON.stringify(item))
            } catch (retryError) {
              console.error('Local storage quota exceeded:', retryError)
            }
          } else {
            console.error('Local storage set error:', error)
          }
        }
      },
      
      cleanup() {
        const now = Date.now()
        const items = []
        
        // 收集所有项目及其时间戳
        for (let key in localStorage) {
          try {
            const item = JSON.parse(localStorage[key])
            items.push({
              key,
              timestamp: item._timestamp || 0,
              size: localStorage[key].length
            })
          } catch (error) {
            // 删除无效项目
            localStorage.removeItem(key)
          }
        }
        
        // 按时间戳排序,删除最旧的项目
        items.sort((a, b) => a.timestamp - b.timestamp)
        
        let freedSpace = 0
        const targetSpace = this.capacity * 0.2 // 释放20%空间
        
        for (const item of items) {
          localStorage.removeItem(item.key)
          freedSpace += item.size
          
          if (freedSpace >= targetSpace) {
            break
          }
        }
      }
    })
    
    // IndexedDB层
    this.layers.set('indexeddb', {
      priority: 4,
      db: null,
      
      async init() {
        return new Promise((resolve, reject) => {
          const request = indexedDB.open('AppDatabase', 1)
          
          request.onerror = () => reject(request.error)
          request.onsuccess = () => {
            this.db = request.result
            resolve()
          }
          
          request.onupgradeneeded = (event) => {
            const db = event.target.result
            
            // 创建对象存储
            if (!db.objectStoreNames.contains('cache')) {
              const store = db.createObjectStore('cache', { keyPath: 'key' })
              store.createIndex('timestamp', 'timestamp', { unique: false })
              store.createIndex('expires', 'expires', { unique: false })
            }
          }
        })
      },
      
      async get(key) {
        if (!this.db) await this.init()
        
        return new Promise((resolve, reject) => {
          const transaction = this.db.transaction(['cache'], 'readonly')
          const store = transaction.objectStore('cache')
          const request = store.get(key)
          
          request.onerror = () => reject(request.error)
          request.onsuccess = () => {
            const result = request.result
            if (!result) {
              resolve(null)
              return
            }
            
            // 检查过期时间
            if (result.expires && Date.now() > result.expires) {
              this.delete(key)
              resolve(null)
              return
            }
            
            resolve(result.data)
          }
        })
      },
      
      async set(key, data, options = {}) {
        if (!this.db) await this.init()
        
        return new Promise((resolve, reject) => {
          const transaction = this.db.transaction(['cache'], 'readwrite')
          const store = transaction.objectStore('cache')
          
          const item = {
            key,
            data,
            timestamp: Date.now(),
            expires: options.expires || null,
            size: JSON.stringify(data).length
          }
          
          const request = store.put(item)
          
          request.onerror = () => reject(request.error)
          request.onsuccess = () => resolve()
        })
      },
      
      async delete(key) {
        if (!this.db) await this.init()
        
        return new Promise((resolve, reject) => {
          const transaction = this.db.transaction(['cache'], 'readwrite')
          const store = transaction.objectStore('cache')
          const request = store.delete(key)
          
          request.onerror = () => reject(request.error)
          request.onsuccess = () => resolve()
        })
      },
      
      async cleanup() {
        if (!this.db) await this.init()
        
        const now = Date.now()
        const transaction = this.db.transaction(['cache'], 'readwrite')
        const store = transaction.objectStore('cache')
        const index = store.index('expires')
        
        // 删除过期项目
        const range = IDBKeyRange.upperBound(now)
        const request = index.openCursor(range)
        
        request.onsuccess = (event) => {
          const cursor = event.target.result
          if (cursor) {
            cursor.delete()
            cursor.continue()
          }
        }
      }
    })
  }
  
  // 获取数据(按优先级查找)
  async get(key, options = {}) {
    const layers = Array.from(this.layers.entries())
      .sort((a, b) => a[1].priority - b[1].priority)
    
    for (const [name, layer] of layers) {
      try {
        const data = await layer.get(key)
        if (data !== null) {
          // 向上层传播数据
          this.propagateUp(key, data, name)
          return data
        }
      } catch (error) {
        console.error(`Error getting from ${name} layer:`, error)
      }
    }
    
    return null
  }
  
  // 设置数据(写入所有层)
  async set(key, data, options = {}) {
    const promises = []
    
    for (const [name, layer] of this.layers) {
      promises.push(
        layer.set(key, data, options).catch(error => {
          console.error(`Error setting to ${name} layer:`, error)
        })
      )
    }
    
    await Promise.allSettled(promises)
  }
  
  // 向上层传播数据
  async propagateUp(key, data, fromLayer) {
    const layers = Array.from(this.layers.entries())
      .filter(([name]) => name !== fromLayer)
      .sort((a, b) => a[1].priority - b[1].priority)
    
    for (const [name, layer] of layers) {
      if (layer.priority < this.layers.get(fromLayer).priority) {
        try {
          await layer.set(key, data)
        } catch (error) {
          console.error(`Error propagating to ${name} layer:`, error)
        }
      }
    }
  }
  
  // 清理所有层
  async cleanup() {
    const promises = []
    
    for (const [name, layer] of this.layers) {
      if (layer.cleanup) {
        promises.push(
          layer.cleanup().catch(error => {
            console.error(`Error cleaning up ${name} layer:`, error)
          })
        )
      }
    }
    
    await Promise.allSettled(promises)
  }
}

// 在Pinia Store中使用分层持久化
export const usePersistentStore = defineStore('persistent', () => {
  const persistenceManager = new LayeredPersistenceManager()
  
  // 状态
  const data = ref(new Map())
  const loading = ref(false)
  
  // 获取数据
  const getData = async (key) => {
    loading.value = true
    
    try {
      // 首先从内存中查找
      if (data.value.has(key)) {
        return data.value.get(key)
      }
      
      // 从持久化层查找
      const persistedData = await persistenceManager.get(key)
      if (persistedData) {
        data.value.set(key, persistedData)
        return persistedData
      }
      
      return null
    } finally {
      loading.value = false
    }
  }
  
  // 设置数据
  const setData = async (key, value, options = {}) => {
    // 更新内存
    data.value.set(key, value)
    
    // 持久化到所有层
    await persistenceManager.set(key, value, options)
  }
  
  // 删除数据
  const deleteData = async (key) => {
    data.value.delete(key)
    
    // 从所有层删除
    for (const [name, layer] of persistenceManager.layers) {
      if (layer.delete) {
        try {
          await layer.delete(key)
        } catch (error) {
          console.error(`Error deleting from ${name} layer:`, error)
        }
      }
    }
  }
  
  return {
    data: readonly(data),
    loading: readonly(loading),
    getData,
    setData,
    deleteData
  }
})

🔄 离线优先的数据同步

离线数据管理策略

javascript 复制代码
// 离线优先数据管理器
class OfflineFirstDataManager {
  constructor() {
    this.syncQueue = []
    this.conflictResolver = new ConflictResolver()
    this.networkStatus = navigator.onLine
    this.setupNetworkListeners()
    this.setupPeriodicSync()
  }
  
  setupNetworkListeners() {
    window.addEventListener('online', () => {
      this.networkStatus = true
      this.processSyncQueue()
    })
    
    window.addEventListener('offline', () => {
      this.networkStatus = false
    })
  }
  
  setupPeriodicSync() {
    // 注册后台同步
    if ('serviceWorker' in navigator && 'sync' in window.ServiceWorkerRegistration.prototype) {
      navigator.serviceWorker.ready.then(registration => {
        registration.sync.register('background-sync')
      })
    }
    
    // 定期同步(在线时)
    setInterval(() => {
      if (this.networkStatus) {
        this.processSyncQueue()
      }
    }, 30000) // 30秒
  }
  
  // 创建或更新数据
  async createOrUpdate(collection, id, data, options = {}) {
    const operation = {
      id: generateId(),
      type: id ? 'update' : 'create',
      collection,
      entityId: id || generateId(),
      data,
      timestamp: Date.now(),
      clientId: this.getClientId(),
      retry: 0,
      maxRetries: options.maxRetries || 3
    }
    
    // 立即更新本地存储
    await this.updateLocalStorage(collection, operation.entityId, {
      ...data,
      _localId: operation.entityId,
      _lastModified: operation.timestamp,
      _syncStatus: 'pending'
    })
    
    // 添加到同步队列
    this.syncQueue.push(operation)
    
    // 如果在线,立即尝试同步
    if (this.networkStatus) {
      this.processSyncQueue()
    }
    
    return operation.entityId
  }
  
  // 删除数据
  async delete(collection, id) {
    const operation = {
      id: generateId(),
      type: 'delete',
      collection,
      entityId: id,
      timestamp: Date.now(),
      clientId: this.getClientId(),
      retry: 0,
      maxRetries: 3
    }
    
    // 标记为已删除(软删除)
    await this.updateLocalStorage(collection, id, {
      _deleted: true,
      _lastModified: operation.timestamp,
      _syncStatus: 'pending'
    })
    
    this.syncQueue.push(operation)
    
    if (this.networkStatus) {
      this.processSyncQueue()
    }
  }
  
  // 查询数据
  async query(collection, filter = {}) {
    const localData = await this.getLocalData(collection)
    
    // 过滤本地数据
    let results = localData.filter(item => {
      // 排除已删除的项目
      if (item._deleted) return false
      
      // 应用过滤条件
      return Object.entries(filter).every(([key, value]) => {
        if (typeof value === 'object' && value.operator) {
          return this.applyOperator(item[key], value.operator, value.value)
        }
        return item[key] === value
      })
    })
    
    // 如果在线,尝试从服务器获取最新数据
    if (this.networkStatus) {
      try {
        const serverData = await this.fetchFromServer(collection, filter)
        results = await this.mergeWithServerData(results, serverData)
      } catch (error) {
        console.warn('Failed to fetch from server, using local data:', error)
      }
    }
    
    return results
  }
  
  // 处理同步队列
  async processSyncQueue() {
    if (!this.networkStatus || this.syncQueue.length === 0) {
      return
    }
    
    const operations = [...this.syncQueue]
    this.syncQueue = []
    
    for (const operation of operations) {
      try {
        await this.syncOperation(operation)
      } catch (error) {
        console.error('Sync operation failed:', error)
        
        // 重试逻辑
        if (operation.retry < operation.maxRetries) {
          operation.retry++
          this.syncQueue.push(operation)
        } else {
          // 标记为同步失败
          await this.markSyncFailed(operation)
        }
      }
    }
  }
  
  // 同步单个操作
  async syncOperation(operation) {
    const { type, collection, entityId, data } = operation
    
    switch (type) {
      case 'create':
        const created = await this.createOnServer(collection, data)
        await this.updateLocalAfterSync(collection, entityId, created, 'synced')
        break
        
      case 'update':
        const updated = await this.updateOnServer(collection, entityId, data)
        await this.updateLocalAfterSync(collection, entityId, updated, 'synced')
        break
        
      case 'delete':
        await this.deleteOnServer(collection, entityId)
        await this.removeFromLocal(collection, entityId)
        break
    }
  }
  
  // 与服务器数据合并
  async mergeWithServerData(localData, serverData) {
    const merged = new Map()
    
    // 添加本地数据
    localData.forEach(item => {
      merged.set(item._localId || item.id, item)
    })
    
    // 合并服务器数据
    for (const serverItem of serverData) {
      const localItem = merged.get(serverItem.id)
      
      if (!localItem) {
        // 服务器有新数据
        merged.set(serverItem.id, {
          ...serverItem,
          _syncStatus: 'synced'
        })
      } else if (localItem._syncStatus === 'synced') {
        // 检查服务器数据是否更新
        if (serverItem.updatedAt > localItem.updatedAt) {
          merged.set(serverItem.id, {
            ...serverItem,
            _syncStatus: 'synced'
          })
        }
      } else {
        // 本地有未同步的更改,需要冲突解决
        const resolved = await this.conflictResolver.resolve(
          localItem,
          serverItem,
          'merge' // 默认合并策略
        )
        
        merged.set(serverItem.id, {
          ...resolved,
          _syncStatus: 'conflict_resolved'
        })
      }
    }
    
    return Array.from(merged.values())
  }
  
  // 本地存储操作
  async updateLocalStorage(collection, id, data) {
    const db = await this.getIndexedDB()
    const transaction = db.transaction([collection], 'readwrite')
    const store = transaction.objectStore(collection)
    
    await new Promise((resolve, reject) => {
      const request = store.put({ ...data, id })
      request.onsuccess = () => resolve()
      request.onerror = () => reject(request.error)
    })
  }
  
  async getLocalData(collection) {
    const db = await this.getIndexedDB()
    const transaction = db.transaction([collection], 'readonly')
    const store = transaction.objectStore(collection)
    
    return new Promise((resolve, reject) => {
      const request = store.getAll()
      request.onsuccess = () => resolve(request.result)
      request.onerror = () => reject(request.error)
    })
  }
  
  // 服务器操作
  async createOnServer(collection, data) {
    const response = await fetch(`/api/${collection}`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    })
    
    if (!response.ok) {
      throw new Error(`Server create failed: ${response.statusText}`)
    }
    
    return response.json()
  }
  
  async updateOnServer(collection, id, data) {
    const response = await fetch(`/api/${collection}/${id}`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    })
    
    if (!response.ok) {
      throw new Error(`Server update failed: ${response.statusText}`)
    }
    
    return response.json()
  }
  
  async deleteOnServer(collection, id) {
    const response = await fetch(`/api/${collection}/${id}`, {
      method: 'DELETE'
    })
    
    if (!response.ok) {
      throw new Error(`Server delete failed: ${response.statusText}`)
    }
  }
  
  async fetchFromServer(collection, filter) {
    const params = new URLSearchParams(filter)
    const response = await fetch(`/api/${collection}?${params}`)
    
    if (!response.ok) {
      throw new Error(`Server fetch failed: ${response.statusText}`)
    }
    
    return response.json()
  }
  
  getClientId() {
    let clientId = localStorage.getItem('clientId')
    if (!clientId) {
      clientId = generateId()
      localStorage.setItem('clientId', clientId)
    }
    return clientId
  }
  
  async getIndexedDB() {
    // IndexedDB初始化逻辑
    // 返回数据库连接
  }
  
  applyOperator(value, operator, operand) {
    switch (operator) {
      case 'eq': return value === operand
      case 'ne': return value !== operand
      case 'gt': return value > operand
      case 'gte': return value >= operand
      case 'lt': return value < operand
      case 'lte': return value <= operand
      case 'in': return operand.includes(value)
      case 'contains': return value.includes(operand)
      default: return true
    }
  }
}

// 冲突解决器
class ConflictResolver {
  constructor() {
    this.strategies = new Map()
    this.setupDefaultStrategies()
  }
  
  setupDefaultStrategies() {
    // 服务器优先策略
    this.strategies.set('server-wins', (local, server) => server)
    
    // 客户端优先策略
    this.strategies.set('client-wins', (local, server) => local)
    
    // 最后修改时间优先
    this.strategies.set('last-modified-wins', (local, server) => {
      const localTime = new Date(local.updatedAt || local._lastModified)
      const serverTime = new Date(server.updatedAt)
      return localTime > serverTime ? local : server
    })
    
    // 字段级合并策略
    this.strategies.set('merge', (local, server) => {
      const merged = { ...server }
      
      // 保留本地的未同步更改
      Object.keys(local).forEach(key => {
        if (key.startsWith('_') || local[key] !== server[key]) {
          // 对于特定字段,使用时间戳比较
          if (this.hasTimestamp(local, key) && this.hasTimestamp(server, key)) {
            const localTime = this.getFieldTimestamp(local, key)
            const serverTime = this.getFieldTimestamp(server, key)
            
            if (localTime > serverTime) {
              merged[key] = local[key]
            }
          } else {
            merged[key] = local[key]
          }
        }
      })
      
      return merged
    })
  }
  
  async resolve(local, server, strategy = 'merge') {
    const resolver = this.strategies.get(strategy)
    if (!resolver) {
      throw new Error(`Unknown conflict resolution strategy: ${strategy}`)
    }
    
    return resolver(local, server)
  }
  
  hasTimestamp(obj, field) {
    return obj[`${field}_timestamp`] !== undefined
  }
  
  getFieldTimestamp(obj, field) {
    return obj[`${field}_timestamp`] || obj._lastModified || 0
  }
}

// 在Pinia Store中使用离线优先管理
export const useOfflineStore = defineStore('offline', () => {
  const dataManager = new OfflineFirstDataManager()
  
  // 状态
  const items = ref([])
  const syncStatus = ref('idle') // 'idle' | 'syncing' | 'error'
  const pendingOperations = ref(0)
  
  // 创建项目
  const createItem = async (data) => {
    syncStatus.value = 'syncing'
    pendingOperations.value++
    
    try {
      const id = await dataManager.createOrUpdate('items', null, data)
      
      // 更新本地状态
      items.value.push({
        ...data,
        id,
        _syncStatus: 'pending'
      })
      
      return id
    } finally {
      pendingOperations.value--
      if (pendingOperations.value === 0) {
        syncStatus.value = 'idle'
      }
    }
  }
  
  // 更新项目
  const updateItem = async (id, data) => {
    syncStatus.value = 'syncing'
    pendingOperations.value++
    
    try {
      await dataManager.createOrUpdate('items', id, data)
      
      // 更新本地状态
      const index = items.value.findIndex(item => item.id === id)
      if (index > -1) {
        items.value[index] = {
          ...items.value[index],
          ...data,
          _syncStatus: 'pending'
        }
      }
    } finally {
      pendingOperations.value--
      if (pendingOperations.value === 0) {
        syncStatus.value = 'idle'
      }
    }
  }
  
  // 删除项目
  const deleteItem = async (id) => {
    syncStatus.value = 'syncing'
    pendingOperations.value++
    
    try {
      await dataManager.delete('items', id)
      
      // 从本地状态移除
      items.value = items.value.filter(item => item.id !== id)
    } finally {
      pendingOperations.value--
      if (pendingOperations.value === 0) {
        syncStatus.value = 'idle'
      }
    }
  }
  
  // 查询项目
  const queryItems = async (filter = {}) => {
    try {
      const results = await dataManager.query('items', filter)
      items.value = results
      return results
    } catch (error) {
      syncStatus.value = 'error'
      throw error
    }
  }
  
  // 强制同步
  const forceSync = async () => {
    syncStatus.value = 'syncing'
    
    try {
      await dataManager.processSyncQueue()
      await queryItems() // 重新查询以获取最新数据
    } finally {
      syncStatus.value = 'idle'
    }
  }
  
  return {
    items: readonly(items),
    syncStatus: readonly(syncStatus),
    pendingOperations: readonly(pendingOperations),
    createItem,
    updateItem,
    deleteItem,
    queryItems,
    forceSync
  }
})

🔄 实时数据同步

WebSocket实时同步

javascript 复制代码
// 实时数据同步管理器
class RealTimeSyncManager {
  constructor() {
    this.ws = null
    this.reconnectAttempts = 0
    this.maxReconnectAttempts = 5
    this.reconnectDelay = 1000
    this.subscriptions = new Map()
    this.messageQueue = []
    this.isConnected = false
    
    this.connect()
  }
  
  connect() {
    try {
      this.ws = new WebSocket('ws://localhost:8080/realtime')
      
      this.ws.onopen = () => {
        console.log('WebSocket connected')
        this.isConnected = true
        this.reconnectAttempts = 0
        
        // 发送队列中的消息
        this.flushMessageQueue()
        
        // 重新订阅
        this.resubscribe()
      }
      
      this.ws.onmessage = (event) => {
        this.handleMessage(JSON.parse(event.data))
      }
      
      this.ws.onclose = () => {
        console.log('WebSocket disconnected')
        this.isConnected = false
        this.scheduleReconnect()
      }
      
      this.ws.onerror = (error) => {
        console.error('WebSocket error:', error)
      }
    } catch (error) {
      console.error('Failed to create WebSocket connection:', error)
      this.scheduleReconnect()
    }
  }
  
  scheduleReconnect() {
    if (this.reconnectAttempts < this.maxReconnectAttempts) {
      this.reconnectAttempts++
      const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1)
      
      console.log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`)
      
      setTimeout(() => {
        this.connect()
      }, delay)
    } else {
      console.error('Max reconnection attempts reached')
    }
  }
  
  // 订阅数据变更
  subscribe(collection, filter = {}, callback) {
    const subscriptionId = generateId()
    
    this.subscriptions.set(subscriptionId, {
      collection,
      filter,
      callback
    })
    
    // 发送订阅消息
    this.send({
      type: 'subscribe',
      subscriptionId,
      collection,
      filter
    })
    
    return subscriptionId
  }
  
  // 取消订阅
  unsubscribe(subscriptionId) {
    if (this.subscriptions.has(subscriptionId)) {
      this.subscriptions.delete(subscriptionId)
      
      this.send({
        type: 'unsubscribe',
        subscriptionId
      })
    }
  }
  
  // 发送消息
  send(message) {
    if (this.isConnected && this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(message))
    } else {
      // 添加到队列
      this.messageQueue.push(message)
    }
  }
  
  // 处理接收到的消息
  handleMessage(message) {
    switch (message.type) {
      case 'data_change':
        this.handleDataChange(message)
        break
        
      case 'subscription_confirmed':
        console.log(`Subscription confirmed: ${message.subscriptionId}`)
        break
        
      case 'error':
        console.error('Server error:', message.error)
        break
        
      default:
        console.warn('Unknown message type:', message.type)
    }
  }
  
  // 处理数据变更
  handleDataChange(message) {
    const { subscriptionId, changeType, data, metadata } = message
    const subscription = this.subscriptions.get(subscriptionId)
    
    if (subscription) {
      try {
        subscription.callback({
          type: changeType, // 'create' | 'update' | 'delete'
          data,
          metadata,
          collection: subscription.collection
        })
      } catch (error) {
        console.error('Error in subscription callback:', error)
      }
    }
  }
  
  // 刷新消息队列
  flushMessageQueue() {
    while (this.messageQueue.length > 0) {
      const message = this.messageQueue.shift()
      this.send(message)
    }
  }
  
  // 重新订阅
  resubscribe() {
    for (const [subscriptionId, subscription] of this.subscriptions) {
      this.send({
        type: 'subscribe',
        subscriptionId,
        collection: subscription.collection,
        filter: subscription.filter
      })
    }
  }
  
  // 关闭连接
  close() {
    if (this.ws) {
      this.ws.close()
    }
  }
}

// 在Pinia Store中使用实时同步
export const useRealTimeStore = defineStore('realtime', () => {
  const syncManager = new RealTimeSyncManager()
  
  // 状态
  const items = ref([])
  const connectionStatus = ref('connecting')
  const subscriptions = ref(new Map())
  
  // 监听连接状态
  watch(
    () => syncManager.isConnected,
    (isConnected) => {
      connectionStatus.value = isConnected ? 'connected' : 'disconnected'
    }
  )
  
  // 订阅集合变更
  const subscribeToCollection = (collection, filter = {}) => {
    const subscriptionId = syncManager.subscribe(collection, filter, (change) => {
      handleDataChange(change)
    })
    
    subscriptions.value.set(subscriptionId, {
      collection,
      filter,
      createdAt: Date.now()
    })
    
    return subscriptionId
  }
  
  // 处理数据变更
  const handleDataChange = (change) => {
    const { type, data, collection } = change
    
    switch (type) {
      case 'create':
        // 检查是否已存在
        if (!items.value.find(item => item.id === data.id)) {
          items.value.push(data)
        }
        break
        
      case 'update':
        const updateIndex = items.value.findIndex(item => item.id === data.id)
        if (updateIndex > -1) {
          items.value[updateIndex] = { ...items.value[updateIndex], ...data }
        }
        break
        
      case 'delete':
        items.value = items.value.filter(item => item.id !== data.id)
        break
    }
    
    // 发布本地事件
    eventBus.publish('DataChanged', {
      collection,
      type,
      data
    })
  }
  
  // 取消订阅
  const unsubscribeFromCollection = (subscriptionId) => {
    syncManager.unsubscribe(subscriptionId)
    subscriptions.value.delete(subscriptionId)
  }
  
  // 清理所有订阅
  const cleanup = () => {
    for (const subscriptionId of subscriptions.value.keys()) {
      syncManager.unsubscribe(subscriptionId)
    }
    subscriptions.value.clear()
    syncManager.close()
  }
  
  // 组件卸载时清理
  onBeforeUnmount(() => {
    cleanup()
  })
  
  return {
    items: readonly(items),
    connectionStatus: readonly(connectionStatus),
    subscriptions: readonly(subscriptions),
    subscribeToCollection,
    unsubscribeFromCollection,
    cleanup
  }
})

📊 数据版本控制和增量同步

版本控制系统

javascript 复制代码
// 数据版本控制管理器
class DataVersionManager {
  constructor() {
    this.versionStore = new Map()
    this.changeLog = []
    this.maxChangeLogSize = 1000
  }
  
  // 创建数据版本
  createVersion(entityId, data, metadata = {}) {
    const version = {
      id: generateId(),
      entityId,
      data: this.deepClone(data),
      version: this.getNextVersion(entityId),
      timestamp: Date.now(),
      author: metadata.author || 'system',
      comment: metadata.comment || '',
      checksum: this.calculateChecksum(data)
    }
    
    // 存储版本
    if (!this.versionStore.has(entityId)) {
      this.versionStore.set(entityId, [])
    }
    
    this.versionStore.get(entityId).push(version)
    
    // 记录变更日志
    this.addToChangeLog({
      type: 'version_created',
      entityId,
      versionId: version.id,
      timestamp: version.timestamp
    })
    
    return version
  }
  
  // 获取实体的所有版本
  getVersions(entityId) {
    return this.versionStore.get(entityId) || []
  }
  
  // 获取特定版本
  getVersion(entityId, versionNumber) {
    const versions = this.getVersions(entityId)
    return versions.find(v => v.version === versionNumber)
  }
  
  // 获取最新版本
  getLatestVersion(entityId) {
    const versions = this.getVersions(entityId)
    return versions.length > 0 ? versions[versions.length - 1] : null
  }
  
  // 比较版本差异
  compareVersions(entityId, fromVersion, toVersion) {
    const from = this.getVersion(entityId, fromVersion)
    const to = this.getVersion(entityId, toVersion)
    
    if (!from || !to) {
      throw new Error('Version not found')
    }
    
    return this.calculateDiff(from.data, to.data)
  }
  
  // 应用增量更新
  applyDelta(entityId, baseVersion, delta) {
    const base = this.getVersion(entityId, baseVersion)
    if (!base) {
      throw new Error('Base version not found')
    }
    
    const updatedData = this.applyDeltaToData(base.data, delta)
    
    return this.createVersion(entityId, updatedData, {
      comment: `Applied delta from version ${baseVersion}`
    })
  }
  
  // 回滚到指定版本
  rollback(entityId, targetVersion) {
    const target = this.getVersion(entityId, targetVersion)
    if (!target) {
      throw new Error('Target version not found')
    }
    
    return this.createVersion(entityId, target.data, {
      comment: `Rolled back to version ${targetVersion}`
    })
  }
  
  // 计算数据差异
  calculateDiff(oldData, newData) {
    const diff = {
      added: {},
      modified: {},
      deleted: {}
    }
    
    // 检查新增和修改
    for (const key in newData) {
      if (!(key in oldData)) {
        diff.added[key] = newData[key]
      } else if (JSON.stringify(oldData[key]) !== JSON.stringify(newData[key])) {
        diff.modified[key] = {
          old: oldData[key],
          new: newData[key]
        }
      }
    }
    
    // 检查删除
    for (const key in oldData) {
      if (!(key in newData)) {
        diff.deleted[key] = oldData[key]
      }
    }
    
    return diff
  }
  
  // 应用差异到数据
  applyDeltaToData(baseData, delta) {
    const result = this.deepClone(baseData)
    
    // 应用新增
    Object.assign(result, delta.added)
    
    // 应用修改
    for (const key in delta.modified) {
      result[key] = delta.modified[key].new
    }
    
    // 应用删除
    for (const key in delta.deleted) {
      delete result[key]
    }
    
    return result
  }
  
  // 获取下一个版本号
  getNextVersion(entityId) {
    const versions = this.getVersions(entityId)
    return versions.length > 0 ? versions[versions.length - 1].version + 1 : 1
  }
  
  // 计算校验和
  calculateChecksum(data) {
    const str = JSON.stringify(data)
    let hash = 0
    
    for (let i = 0; i < str.length; i++) {
      const char = str.charCodeAt(i)
      hash = ((hash << 5) - hash) + char
      hash = hash & hash // 转换为32位整数
    }
    
    return hash.toString(16)
  }
  
  // 深度克隆
  deepClone(obj) {
    return JSON.parse(JSON.stringify(obj))
  }
  
  // 添加到变更日志
  addToChangeLog(entry) {
    this.changeLog.push(entry)
    
    // 限制日志大小
    if (this.changeLog.length > this.maxChangeLogSize) {
      this.changeLog.shift()
    }
  }
  
  // 获取变更日志
  getChangeLog(filter = {}) {
    let log = this.changeLog
    
    if (filter.entityId) {
      log = log.filter(entry => entry.entityId === filter.entityId)
    }
    
    if (filter.since) {
      log = log.filter(entry => entry.timestamp >= filter.since)
    }
    
    return log
  }
}

// 增量同步管理器
class IncrementalSyncManager {
  constructor(versionManager) {
    this.versionManager = versionManager
    this.syncState = new Map() // 存储每个实体的同步状态
  }
  
  // 获取增量更新
  async getIncrementalUpdates(entityId, lastSyncVersion) {
    const versions = this.versionManager.getVersions(entityId)
    const updatesNeeded = versions.filter(v => v.version > lastSyncVersion)
    
    if (updatesNeeded.length === 0) {
      return { hasUpdates: false, updates: [] }
    }
    
    // 计算增量更新
    const updates = []
    let currentVersion = lastSyncVersion
    
    for (const version of updatesNeeded) {
      if (currentVersion === 0) {
        // 首次同步,发送完整数据
        updates.push({
          type: 'full',
          version: version.version,
          data: version.data,
          timestamp: version.timestamp
        })
      } else {
        // 计算增量
        const previousVersion = this.versionManager.getVersion(entityId, currentVersion)
        if (previousVersion) {
          const delta = this.versionManager.calculateDiff(previousVersion.data, version.data)
          updates.push({
            type: 'delta',
            version: version.version,
            baseVersion: currentVersion,
            delta,
            timestamp: version.timestamp
          })
        }
      }
      
      currentVersion = version.version
    }
    
    return { hasUpdates: true, updates }
  }
  
  // 应用增量更新
  async applyIncrementalUpdates(entityId, updates) {
    let currentData = null
    let currentVersion = 0
    
    // 获取当前状态
    const syncState = this.syncState.get(entityId)
    if (syncState) {
      currentData = syncState.data
      currentVersion = syncState.version
    }
    
    for (const update of updates) {
      if (update.type === 'full') {
        currentData = update.data
        currentVersion = update.version
      } else if (update.type === 'delta') {
        if (currentVersion !== update.baseVersion) {
          throw new Error(`Version mismatch: expected ${update.baseVersion}, got ${currentVersion}`)
        }
        
        currentData = this.versionManager.applyDeltaToData(currentData, update.delta)
        currentVersion = update.version
      }
    }
    
    // 更新同步状态
    this.syncState.set(entityId, {
      data: currentData,
      version: currentVersion,
      lastSync: Date.now()
    })
    
    return {
      data: currentData,
      version: currentVersion
    }
  }
  
  // 检查是否需要同步
  needsSync(entityId, serverVersion) {
    const syncState = this.syncState.get(entityId)
    return !syncState || syncState.version < serverVersion
  }
  
  // 获取同步状态
  getSyncState(entityId) {
    return this.syncState.get(entityId)
  }
  
  // 重置同步状态
  resetSyncState(entityId) {
    this.syncState.delete(entityId)
  }
}

// 在Pinia Store中使用版本控制和增量同步
export const useVersionedStore = defineStore('versioned', () => {
  const versionManager = new DataVersionManager()
  const syncManager = new IncrementalSyncManager(versionManager)
  
  // 状态
  const entities = ref(new Map())
  const syncStatus = ref(new Map())
  
  // 创建或更新实体
  const saveEntity = async (entityId, data, metadata = {}) => {
    // 创建版本
    const version = versionManager.createVersion(entityId, data, metadata)
    
    // 更新本地状态
    entities.value.set(entityId, {
      ...data,
      _version: version.version,
      _lastModified: version.timestamp
    })
    
    // 标记需要同步
    syncStatus.value.set(entityId, 'pending')
    
    return version
  }
  
  // 获取实体
  const getEntity = (entityId) => {
    return entities.value.get(entityId)
  }
  
  // 获取实体版本历史
  const getEntityVersions = (entityId) => {
    return versionManager.getVersions(entityId)
  }
  
  // 同步实体
  const syncEntity = async (entityId) => {
    try {
      syncStatus.value.set(entityId, 'syncing')
      
      // 获取服务器版本信息
      const serverInfo = await fetchServerVersionInfo(entityId)
      
      if (syncManager.needsSync(entityId, serverInfo.version)) {
        // 获取增量更新
        const currentState = syncManager.getSyncState(entityId)
        const lastSyncVersion = currentState ? currentState.version : 0
        
        const serverUpdates = await fetchIncrementalUpdates(entityId, lastSyncVersion)
        
        if (serverUpdates.hasUpdates) {
          // 应用增量更新
          const result = await syncManager.applyIncrementalUpdates(entityId, serverUpdates.updates)
          
          // 更新本地状态
          entities.value.set(entityId, {
            ...result.data,
            _version: result.version,
            _lastSync: Date.now()
          })
        }
      }
      
      syncStatus.value.set(entityId, 'synced')
    } catch (error) {
      syncStatus.value.set(entityId, 'error')
      throw error
    }
  }
  
  // 回滚实体到指定版本
  const rollbackEntity = async (entityId, targetVersion) => {
    const rolledBack = versionManager.rollback(entityId, targetVersion)
    
    entities.value.set(entityId, {
      ...rolledBack.data,
      _version: rolledBack.version,
      _lastModified: rolledBack.timestamp
    })
    
    syncStatus.value.set(entityId, 'pending')
    
    return rolledBack
  }
  
  return {
    entities: readonly(entities),
    syncStatus: readonly(syncStatus),
    saveEntity,
    getEntity,
    getEntityVersions,
    syncEntity,
    rollbackEntity
  }
})

// 辅助函数
async function fetchServerVersionInfo(entityId) {
  const response = await fetch(`/api/entities/${entityId}/version`)
  return response.json()
}

async function fetchIncrementalUpdates(entityId, lastSyncVersion) {
  const response = await fetch(`/api/entities/${entityId}/updates?since=${lastSyncVersion}`)
  return response.json()
}

📝 总结

数据持久化与同步是现代Web应用的核心基础设施。通过本文的学习,你应该掌握了:

持久化架构

  • 多层次数据持久化的设计原则
  • 不同存储层的特点和适用场景
  • 容量管理和数据清理策略

离线优先

  • 离线数据管理的完整方案
  • 同步队列和冲突解决机制
  • 网络状态感知和自动同步

实时同步

  • WebSocket实时数据同步
  • 订阅模式和事件驱动更新
  • 连接管理和错误恢复

版本控制

  • 数据版本控制系统的实现
  • 增量同步和差异计算
  • 回滚和历史管理功能

最佳实践

  • 大规模应用的数据管理架构
  • 性能优化和资源管理
  • 数据一致性和可靠性保证

掌握这些技术将帮助你构建健壮、高性能的数据管理系统,特别是在处理复杂的企业级应用数据需求时。

至此,"路由与状态管理"系列已经完成。这个系列涵盖了从基础的Vue Router使用到复杂的企业级状态管理架构,为构建大型Vue 3应用提供了完整的解决方案。

相关推荐
IT毕设实战小研13 小时前
Java毕业设计选题推荐 |基于SpringBoot的健身爱好线上互动与打卡社交平台系统 互动打卡小程序系统
java·开发语言·vue.js·spring boot·vue·毕业设计·课程设计
第七种黄昏1 天前
大事件项目拆解:登录访问拦截实现详解
前端框架·vue·js
har01d2 天前
在 uniapp 里使用 unocss,vue3 + vite 项目
前端·uni-app·vue·uniapp·unocss
har01d3 天前
【CSS3】录音中。。。
前端·css·vue.js·vue·vue3·css3
柯北(jvxiao)4 天前
Vue vs React 多维度剖析: 哪一个更适合大型项目?
前端·vue·react
晓13134 天前
Vue2篇——第二章 Vue从指令修饰符到侦听器的全面解析(重点)
前端·javascript·vue
Kevin@wust5 天前
axios的封装
前端·vue
夏小花花5 天前
Java 日常开发笔记(小程序页面交互传参-id)
java·微信小程序·vue
狼性书生6 天前
uniapp实现的圆形滚盘组件模板
前端·uni-app·vue·组件