數據存儲
uni-app提供了豐富的數據存儲方案,支持本地存儲、文件存儲、數據庫存儲等多種方式。本文將詳細介紹各種存儲方法的使用和最佳實踐。
基礎存儲API
uni-app提供了一套統一的存儲API,在不同平台上會自動適配到對應的存儲機制:
異步存儲
javascript
// 存儲數據
uni.setStorage({
key: 'userInfo',
data: {
name: '張三',
age: 25,
email: 'zhangsan@example.com'
},
success: function() {
console.log('存儲成功')
},
fail: function(err) {
console.error('存儲失敗:', err)
}
})
// 獲取數據
uni.getStorage({
key: 'userInfo',
success: function(res) {
console.log('用戶信息:', res.data)
},
fail: function(err) {
console.error('獲取失敗:', err)
}
})
// 移除數據
uni.removeStorage({
key: 'userInfo',
success: function() {
console.log('移除成功')
},
fail: function(err) {
console.error('移除失敗:', err)
}
})
// 清空所有存儲
uni.clearStorage({
success: function() {
console.log('清空成功')
},
fail: function(err) {
console.error('清空失敗:', err)
}
})
// 獲取存儲信息
uni.getStorageInfo({
success: function(res) {
console.log('存儲信息:', res)
console.log('已使用空間:', res.currentSize)
console.log('總空間:', res.limitSize)
console.log('所有鍵名:', res.keys)
}
})同步存儲
javascript
// 同步存儲數據
try {
uni.setStorageSync('userInfo', {
name: '李四',
age: 30,
email: 'lisi@example.com'
})
console.log('存儲成功')
} catch (e) {
console.error('存儲失敗:', e)
}
// 同步獲取數據
try {
const userInfo = uni.getStorageSync('userInfo')
if (userInfo) {
console.log('用戶信息:', userInfo)
} else {
console.log('未找到用戶信息')
}
} catch (e) {
console.error('獲取失敗:', e)
}
// 同步移除數據
try {
uni.removeStorageSync('userInfo')
console.log('移除成功')
} catch (e) {
console.error('移除失敗:', e)
}
// 同步清空所有存儲
try {
uni.clearStorageSync()
console.log('清空成功')
} catch (e) {
console.error('清空失敗:', e)
}
// 同步獲取存儲信息
try {
const res = uni.getStorageInfoSync()
console.log('存儲信息:', res)
} catch (e) {
console.error('獲取存儲信息失敗:', e)
}存儲限制
不同平台對存儲空間有不同的限制:
各平台存儲限制
| 平台 | 存儲限制 | 說明 |
|---|---|---|
| 微信小程序 | 10MB | 單個key最大1MB |
| 支付寶小程序 | 10MB | 單個key最大200KB |
| 百度小程序 | 10MB | 單個key最大1MB |
| 字節跳動小程序 | 10MB | 單個key最大1MB |
| H5 | 5-10MB | 取決於瀏覽器localStorage限制 |
| App | 無限制 | 受設備存儲空間限制 |
支持的數據類型
uni-app存儲支持以下數據類型:
javascript
// 字符串
uni.setStorageSync('string', 'Hello World')
// 數字
uni.setStorageSync('number', 123)
// 布爾值
uni.setStorageSync('boolean', true)
// 對象
uni.setStorageSync('object', {
name: '張三',
age: 25,
hobbies: ['讀書', '運動', '旅行']
})
// 數組
uni.setStorageSync('array', [1, 2, 3, 4, 5])
// 日期對象
uni.setStorageSync('date', new Date())
// null值
uni.setStorageSync('null', null)注意:存儲的數據會自動進行JSON序列化和反序列化,因此不支持函數、undefined等特殊類型。
存儲封裝與管理
為了更好地管理存儲數據,建議封裝一個存儲管理模塊:
javascript
// storage.js
/**
* 存儲管理類
*/
class Storage {
/**
* 設置存儲
* @param {string} key 鍵名
* @param {any} data 數據
* @param {number} expires 過期時間(毫秒),不設置則永不過期
* @returns {boolean} 是否成功
*/
set(key, data, expires) {
try {
const item = {
data,
expires: expires ? Date.now() + expires : null,
timestamp: Date.now()
}
uni.setStorageSync(key, item)
return true
} catch (e) {
console.error('存儲失敗:', e)
return false
}
}
/**
* 獲取存儲
* @param {string} key 鍵名
* @param {any} defaultValue 默認值,當獲取失敗或已過期時返回
* @returns {any} 存儲的數據
*/
get(key, defaultValue = null) {
try {
const item = uni.getStorageSync(key)
if (!item) {
return defaultValue
}
// 檢查是否過期
if (item.expires && item.expires < Date.now()) {
this.remove(key)
return defaultValue
}
return item.data
} catch (e) {
console.error('獲取存儲失敗:', e)
return defaultValue
}
}
/**
* 移除存儲
* @param {string} key 鍵名
* @returns {boolean} 是否成功
*/
remove(key) {
try {
uni.removeStorageSync(key)
return true
} catch (e) {
console.error('移除存儲失敗:', e)
return false
}
}
/**
* 清空所有存儲
* @returns {boolean} 是否成功
*/
clear() {
try {
uni.clearStorageSync()
return true
} catch (e) {
console.error('清空存儲失敗:', e)
return false
}
}
/**
* 獲取所有鍵名
* @returns {Array<string>} 鍵名數組
*/
keys() {
try {
const res = uni.getStorageInfoSync()
return res.keys || []
} catch (e) {
console.error('獲取鍵名失敗:', e)
return []
}
}
/**
* 獲取存儲信息
* @returns {Object} 存儲信息
*/
info() {
try {
return uni.getStorageInfoSync()
} catch (e) {
console.error('獲取存儲信息失敗:', e)
return {
keys: [],
currentSize: 0,
limitSize: 0
}
}
}
/**
* 檢查鍵是否存在
* @param {string} key 鍵名
* @returns {boolean} 是否存在
*/
has(key) {
try {
const item = uni.getStorageSync(key)
if (!item) {
return false
}
// 檢查是否過期
if (item.expires && item.expires < Date.now()) {
this.remove(key)
return false
}
return true
} catch (e) {
console.error('檢查鍵存在失敗:', e)
return false
}
}
/**
* 獲取當前存儲大小
* @returns {number} 存儲大小(KB)
*/
size() {
try {
const res = uni.getStorageInfoSync()
return res.currentSize || 0
} catch (e) {
console.error('獲取存儲大小失敗:', e)
return 0
}
}
/**
* 獲取存儲限制大小
* @returns {number} 限制大小(KB)
*/
limitSize() {
try {
const res = uni.getStorageInfoSync()
return res.limitSize || 0
} catch (e) {
console.error('獲取存儲限制失敗:', e)
return 0
}
}
/**
* 批量設置存儲
* @param {Object} data 鍵值對對象
* @param {number} expires 過期時間(毫秒)
* @returns {boolean} 是否全部成功
*/
setAll(data, expires) {
let success = true
Object.keys(data).forEach(key => {
if (!this.set(key, data[key], expires)) {
success = false
}
})
return success
}
/**
* 批量獲取存儲
* @param {Array<string>} keys 鍵名數組
* @param {any} defaultValue 默認值
* @returns {Object} 鍵值對對象
*/
getAll(keys, defaultValue = null) {
const result = {}
keys.forEach(key => {
result[key] = this.get(key, defaultValue)
})
return result
}
/**
* 批量移除存儲
* @param {Array<string>} keys 鍵名數組
* @returns {boolean} 是否全部成功
*/
removeAll(keys) {
let success = true
keys.forEach(key => {
if (!this.remove(key)) {
success = false
}
})
return success
}
/**
* 清理過期數據
* @returns {number} 清理的數量
*/
clearExpired() {
let count = 0
try {
const keys = this.keys()
keys.forEach(key => {
const item = uni.getStorageSync(key)
if (item && item.expires && item.expires < Date.now()) {
this.remove(key)
count++
}
})
} catch (e) {
console.error('清理過期數據失敗:', e)
}
return count
}
}
// 創建存儲實例
const storage = new Storage()
export default storage使用封裝的存儲管理:
javascript
import storage from '@/utils/storage'
// 設置數據(30分鐘過期)
storage.set('userToken', 'abc123', 30 * 60 * 1000)
// 設置永不過期的數據
storage.set('userSettings', {
theme: 'dark',
language: 'zh-CN'
})
// 獲取數據
const token = storage.get('userToken')
const settings = storage.get('userSettings', {})
// 檢查數據是否存在
if (storage.has('userToken')) {
console.log('用戶已登錄')
}
// 批量操作
storage.setAll({
'key1': 'value1',
'key2': 'value2',
'key3': 'value3'
}, 60 * 1000) // 1分鐘過期
const data = storage.getAll(['key1', 'key2', 'key3'])
// 清理過期數據
const expiredCount = storage.clearExpired()
console.log(`清理了${expiredCount}個過期數據`)
// 獲取存儲信息
const info = storage.info()
console.log(`已使用:${info.currentSize}KB,總容量:${info.limitSize}KB`)加密存儲
對於敏感數據,建議使用加密存儲:
javascript
// crypto-storage.js
import CryptoJS from 'crypto-js'
/**
* 加密存儲類
*/
class CryptoStorage {
constructor(secretKey) {
this.secretKey = secretKey || 'default-secret-key'
}
/**
* 加密數據
* @param {any} data 要加密的數據
* @returns {string} 加密後的字符串
*/
encrypt(data) {
try {
const jsonStr = JSON.stringify(data)
const encrypted = CryptoJS.AES.encrypt(jsonStr, this.secretKey).toString()
return encrypted
} catch (e) {
console.error('加密失敗:', e)
return null
}
}
/**
* 解密數據
* @param {string} encryptedData 加密的數據
* @returns {any} 解密後的數據
*/
decrypt(encryptedData) {
try {
const bytes = CryptoJS.AES.decrypt(encryptedData, this.secretKey)
const decryptedStr = bytes.toString(CryptoJS.enc.Utf8)
return JSON.parse(decryptedStr)
} catch (e) {
console.error('解密失敗:', e)
return null
}
}
/**
* 設置加密存儲
* @param {string} key 鍵名
* @param {any} data 數據
* @param {number} expires 過期時間(毫秒)
* @returns {boolean} 是否成功
*/
set(key, data, expires) {
try {
const item = {
data,
expires: expires ? Date.now() + expires : null,
timestamp: Date.now()
}
const encryptedData = this.encrypt(item)
if (encryptedData) {
uni.setStorageSync(`encrypted_${key}`, encryptedData)
return true
}
return false
} catch (e) {
console.error('設置加密存儲失敗:', e)
return false
}
}
/**
* 獲取加密存儲
* @param {string} key 鍵名
* @param {any} defaultValue 默認值
* @returns {any} 存儲的數據
*/
get(key, defaultValue = null) {
try {
const encryptedData = uni.getStorageSync(`encrypted_${key}`)
if (!encryptedData) {
return defaultValue
}
const item = this.decrypt(encryptedData)
if (!item) {
return defaultValue
}
// 檢查是否過期
if (item.expires && item.expires < Date.now()) {
this.remove(key)
return defaultValue
}
return item.data
} catch (e) {
console.error('獲取加密存儲失敗:', e)
return defaultValue
}
}
/**
* 移除加密存儲
* @param {string} key 鍵名
* @returns {boolean} 是否成功
*/
remove(key) {
try {
uni.removeStorageSync(`encrypted_${key}`)
return true
} catch (e) {
console.error('刪除加密存儲失敗:', e)
return false
}
}
/**
* 檢查加密鍵是否存在
* @param {string} key 鍵名
* @returns {boolean} 是否存在
*/
has(key) {
try {
const res = uni.getStorageInfoSync()
return res.keys.includes(`encrypted_${key}`)
} catch (e) {
console.error('檢查加密鍵失敗:', e)
return false
}
}
/**
* 更改密鑰(會重新加密所有數據)
* @param {string} newSecretKey 新密鑰
* @returns {boolean} 是否成功
*/
changeSecretKey(newSecretKey) {
if (!newSecretKey) {
return false
}
try {
const res = uni.getStorageInfoSync()
const encryptedKeys = res.keys.filter(key => key.startsWith('encrypted_'))
// 獲取所有加密數據
const allData = {}
encryptedKeys.forEach(key => {
const originalKey = key.replace('encrypted_', '')
const encryptedData = uni.getStorageSync(key)
const item = this.decrypt(encryptedData)
if (item) {
allData[originalKey] = {
data: item.data,
expires: item.expires
}
}
})
// 更改密鑰
const oldSecretKey = this.secretKey
this.secretKey = newSecretKey
// 重新加密所有數據
Object.keys(allData).forEach(key => {
const item = allData[key]
const encryptedData = this.encrypt({
data: item.data,
expires: item.expires
})
uni.setStorageSync(`encrypted_${key}`, encryptedData)
})
return true
} catch (e) {
// 恢復舊密鑰
this.secretKey = oldSecretKey
console.error('更改密鑰失敗:', e)
return false
}
}
}
// 創建加密存儲實例
const cryptoStorage = new CryptoStorage('your-secret-key')
export default cryptoStorage使用加密存儲:
javascript
import cryptoStorage from '@/utils/crypto-storage'
// 存儲敏感數據(7天過期)
cryptoStorage.set('password', '123456', 7 * 24 * 3600 * 1000)
cryptoStorage.set('token', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...')
// 獲取敏感數據
const password = cryptoStorage.get('password')
const token = cryptoStorage.get('token')
// 更改密鑰
cryptoStorage.changeSecretKey('new-secret-key')文件存儲
對於大型數據或二進制數據,可以使用uni-app提供的文件系統API進行存儲:
保存文件
javascript
// 保存文件到本地
uni.saveFile({
tempFilePath: tempFilePath, // 需要保存的文件的臨時路徑
success: function(res) {
const savedFilePath = res.savedFilePath
console.log('文件保存成功:', savedFilePath)
// 可以將文件路徑存儲起來,以便後續使用
uni.setStorageSync('savedFilePath', savedFilePath)
},
fail: function(err) {
console.error('文件保存失敗:', err)
}
})獲取保存的文件列表
javascript
uni.getSavedFileList({
success: function(res) {
console.log('文件列表:', res.fileList)
// res.fileList是一個數組,包含filePath和createTime等信息
},
fail: function(err) {
console.error('獲取文件列表失敗:', err)
}
})獲取文件信息
javascript
uni.getSavedFileInfo({
filePath: savedFilePath,
success: function(res) {
console.log('文件大小:', res.size)
console.log('創建時間:', res.createTime)
},
fail: function(err) {
console.error('獲取文件信息失敗:', err)
}
})刪除文件
javascript
uni.removeSavedFile({
filePath: savedFilePath,
success: function() {
console.log('文件刪除成功')
},
fail: function(err) {
console.error('文件刪除失敗:', err)
}
})文件管理器
對於App平台,可以使用更強大的文件管理器API:
javascript
// 僅在App平台可用
// #ifdef APP-PLUS
const fs = uni.getFileSystemManager()
// 寫入文件
fs.writeFile({
filePath: `${uni.env.USER_DATA_PATH}/test.txt`,
data: 'Hello, uni-app!',
encoding: 'utf8',
success: function() {
console.log('寫入成功')
},
fail: function(err) {
console.error('寫入失敗:', err)
}
})
// 讀取文件
fs.readFile({
filePath: `${uni.env.USER_DATA_PATH}/test.txt`,
encoding: 'utf8',
success: function(res) {
console.log('文件內容:', res.data)
},
fail: function(err) {
console.error('讀取失敗:', err)
}
})
// 刪除文件
fs.unlink({
filePath: `${uni.env.USER_DATA_PATH}/test.txt`,
success: function() {
console.log('刪除成功')
},
fail: function(err) {
console.error('刪除失敗:', err)
}
})
// #endifIndexedDB存儲
在H5平台,可以使用IndexedDB進行大容量數據存儲:
javascript
// indexed-db.js
/**
* IndexedDB存儲類(僅H5平台可用)
*/
class IndexedDBStorage {
constructor(dbName = 'uniapp-db', version = 1) {
this.dbName = dbName
this.version = version
this.db = null
// 僅在H5平台初始化
// #ifdef H5
this.init()
// #endif
}
/**
* 初始化數據庫
* @returns {Promise} 初始化Promise
*/
init() {
return new Promise((resolve, reject) => {
// 檢查瀏覽器是否支持IndexedDB
if (!window.indexedDB) {
reject(new Error('瀏覽器不支持IndexedDB'))
return
}
const request = window.indexedDB.open(this.dbName, this.version)
request.onerror = (event) => {
console.error('IndexedDB打開失敗:', event)
reject(event)
}
request.onsuccess = (event) => {
this.db = event.target.result
console.log('IndexedDB打開成功')
resolve(this.db)
}
request.onupgradeneeded = (event) => {
const db = event.target.result
// 創建存儲對象
if (!db.objectStoreNames.contains('keyvaluepairs')) {
db.createObjectStore('keyvaluepairs', { keyPath: 'key' })
}
}
})
}
/**
* 確保數據庫已連接
* @returns {Promise} 數據庫連接Promise
*/
ensureDB() {
if (this.db) {
return Promise.resolve(this.db)
}
return this.init()
}
/**
* 設置數據
* @param {string} key 鍵名
* @param {any} data 數據
* @param {number} expires 過期時間(毫秒),不設置則永不過期
* @returns {Promise} 操作Promise
*/
set(key, data, expires) {
return this.ensureDB().then(db => {
return new Promise((resolve, reject) => {
const transaction = db.transaction(['keyvaluepairs'], 'readwrite')
const store = transaction.objectStore('keyvaluepairs')
const item = {
key,
data,
expires: expires ? Date.now() + expires : null,
timestamp: Date.now()
}
const request = store.put(item)
request.onsuccess = () => {
resolve(true)
}
request.onerror = (event) => {
console.error('存儲數據失敗:', event)
reject(event)
}
})
})
}
/**
* 獲取數據
* @param {string} key 鍵名
* @param {any} defaultValue 默認值,當獲取失敗或已過期時返回
* @returns {Promise} 包含數據的Promise
*/
get(key, defaultValue = null) {
return this.ensureDB().then(db => {
return new Promise((resolve, reject) => {
const transaction = db.transaction(['keyvaluepairs'], 'readonly')
const store = transaction.objectStore('keyvaluepairs')
const request = store.get(key)
request.onsuccess = (event) => {
const item = event.target.result
if (!item) {
resolve(defaultValue)
return
}
// 檢查是否過期
if (item.expires && item.expires < Date.now()) {
this.remove(key).then(() => {
resolve(defaultValue)
}).catch(() => {
resolve(defaultValue)
})
return
}
resolve(item.data)
}
request.onerror = (event) => {
console.error('獲取數據失敗:', event)
reject(event)
}
})
})
}
/**
* 移除數據
* @param {string} key 鍵名
* @returns {Promise} 操作Promise
*/
remove(key) {
return this.ensureDB().then(db => {
return new Promise((resolve, reject) => {
const transaction = db.transaction(['keyvaluepairs'], 'readwrite')
const store = transaction.objectStore('keyvaluepairs')
const request = store.delete(key)
request.onsuccess = () => {
resolve(true)
}
request.onerror = (event) => {
console.error('刪除數據失敗:', event)
reject(event)
}
})
})
}
/**
* 清空所有數據
* @returns {Promise} 操作Promise
*/
clear() {
return this.ensureDB().then(db => {
return new Promise((resolve, reject) => {
const transaction = db.transaction(['keyvaluepairs'], 'readwrite')
const store = transaction.objectStore('keyvaluepairs')
const request = store.clear()
request.onsuccess = () => {
resolve(true)
}
request.onerror = (event) => {
console.error('清空數據失敗:', event)
reject(event)
}
})
})
}
/**
* 獲取所有鍵
* @returns {Promise} 包含鍵數組的Promise
*/
keys() {
return this.ensureDB().then(db => {
return new Promise((resolve, reject) => {
const transaction = db.transaction(['keyvaluepairs'], 'readonly')
const store = transaction.objectStore('keyvaluepairs')
const request = store.getAllKeys()
request.onsuccess = (event) => {
resolve(event.target.result || [])
}
request.onerror = (event) => {
console.error('獲取鍵失敗:', event)
reject(event)
}
})
})
}
/**
* 獲取所有數據
* @returns {Promise} 包含所有數據的Promise
*/
getAll() {
return this.ensureDB().then(db => {
return new Promise((resolve, reject) => {
const transaction = db.transaction(['keyvaluepairs'], 'readonly')
const store = transaction.objectStore('keyvaluepairs')
const request = store.getAll()
request.onsuccess = (event) => {
const items = event.target.result || []
const result = {}
items.forEach(item => {
// 檢查是否過期
if (item.expires && item.expires < Date.now()) {
this.remove(item.key)
} else {
result[item.key] = item.data
}
})
resolve(result)
}
request.onerror = (event) => {
console.error('獲取所有數據失敗:', event)
reject(event)
}
})
})
}
}
// 創建IndexedDB存儲實例
// #ifdef H5
const indexedDBStorage = new IndexedDBStorage()
export default indexedDBStorage
// #endif使用IndexedDB存儲:
javascript
// #ifdef H5
import indexedDBStorage from '@/utils/indexed-db'
// 存儲數據
indexedDBStorage.set('largeData', largeDataObject)
.then(() => {
console.log('大型數據存儲成功')
})
.catch(err => {
console.error('存儲失敗:', err)
})
// 獲取數據
indexedDBStorage.get('largeData')
.then(data => {
console.log('獲取大型數據成功:', data)
})
.catch(err => {
console.error('獲取失敗:', err)
})
// #endif數據同步與備份
為了確保用戶數據安全,可以實現數據同步與備份功能:
雲端同步
javascript
// sync.js
import request from '@/utils/request'
import storage from '@/utils/storage'
/**
* 數據同步類
*/
class DataSync {
constructor() {
this.syncKeys = ['userSettings', 'favorites', 'history']
this.lastSyncTime = storage.get('lastSyncTime', 0)
}
/**
* 上傳數據到雲端
* @returns {Promise} 同步Promise
*/
uploadToCloud() {
// 獲取需要同步的數據
const syncData = {}
this.syncKeys.forEach(key => {
syncData[key] = storage.get(key)
})
// 添加同步時間戳
syncData.timestamp = Date.now()
// 上傳到服務器
return request.post('/user/sync', syncData)
.then(() => {
// 更新最後同步時間
this.lastSyncTime = syncData.timestamp
storage.set('lastSyncTime', this.lastSyncTime)
console.log('數據上傳成功')
return true
})
.catch(err => {
console.error('數據上傳失敗:', err)
throw err
})
}
/**
* 從雲端下載數據
* @returns {Promise} 同步Promise
*/
downloadFromCloud() {
// 獲取服務器數據
return request.get('/user/sync', { lastSyncTime: this.lastSyncTime })
.then(data => {
if (!data || !data.timestamp) {
console.log('沒有新數據需要同步')
return false
}
// 檢查服務器數據是否比本地新
if (data.timestamp <= this.lastSyncTime) {
console.log('本地數據已是最新')
return false
}
// 更新本地數據
Object.keys(data).forEach(key => {
if (key !== 'timestamp' && this.syncKeys.includes(key)) {
storage.set(key, data[key])
}
})
// 更新最後同步時間
this.lastSyncTime = data.timestamp
storage.set('lastSyncTime', this.lastSyncTime)
console.log('數據下載成功')
return true
})
.catch(err => {
console.error('數據下載失敗:', err)
throw err
})
}
/**
* 雙向同步數據
* @returns {Promise} 同步Promise
*/
sync() {
return this.downloadFromCloud()
.then(() => {
return this.uploadToCloud()
})
.then(() => {
console.log('數據同步完成')
return true
})
.catch(err => {
console.error('數據同步失敗:', err)
throw err
})
}
/**
* 自動同步(定時執行)
* @param {number} interval 同步間隔(毫秒)
*/
startAutoSync(interval = 5 * 60 * 1000) {
// 先執行一次同步
this.sync()
.catch(() => {
// 忽略錯誤
})
// 設置定時同步
setInterval(() => {
this.sync()
.catch(() => {
// 忽略錯誤
})
}, interval)
}
}
// 創建數據同步實例
const dataSync = new DataSync()
export default dataSync使用數據同步:
javascript
import dataSync from '@/utils/sync'
// 手動同步
dataSync.sync()
.then(() => {
uni.showToast({
title: '同步成功',
icon: 'success'
})
})
.catch(() => {
uni.showToast({
title: '同步失敗',
icon: 'none'
})
})
// 啟動自動同步(每5分鐘)
dataSync.startAutoSync()本地備份與恢復
javascript
// backup.js
import storage from '@/utils/storage'
/**
* 數據備份類
*/
class DataBackup {
/**
* 創建備份
* @param {Array<string>} keys 要備份的鍵名數組,不提供則備份所有
* @returns {Object} 備份數據
*/
createBackup(keys) {
try {
// 獲取所有鍵或指定鍵
const backupKeys = keys || storage.keys()
// 獲取數據
const backupData = {}
backupKeys.forEach(key => {
const value = storage.get(key)
if (value !== null) {
backupData[key] = value
}
})
// 添加備份信息
const backupInfo = {
timestamp: Date.now(),
version: '1.0.0',
keys: Object.keys(backupData),
count: Object.keys(backupData).length
}
const backup = {
info: backupInfo,
data: backupData
}
return backup
} catch (e) {
console.error('創建備份失敗:', e)
return null
}
}
/**
* 保存備份到文件
* @param {Object} backup 備份數據
* @returns {Promise} 操作Promise
*/
saveBackupToFile(backup) {
return new Promise((resolve, reject) => {
try {
if (!backup) {
reject(new Error('備份數據為空'))
return
}
// 轉換為JSON字符串
const backupStr = JSON.stringify(backup)
// 僅在App平台可用
// #ifdef APP-PLUS
const fs = uni.getFileSystemManager()
const backupPath = `${uni.env.USER_DATA_PATH}/backup_${backup.info.timestamp}.json`
fs.writeFile({
filePath: backupPath,
data: backupStr,
encoding: 'utf8',
success: () => {
console.log('備份文件保存成功:', backupPath)
resolve(backupPath)
},
fail: (err) => {
console.error('備份文件保存失敗:', err)
reject(err)
}
})
// #endif
// 在H5平台使用下載文件
// #ifdef H5
const blob = new Blob([backupStr], { type: 'application/json' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = `backup_${backup.info.timestamp}.json`
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
resolve(a.download)
// #endif
// 在小程序平台,可以將備份保存到本地存儲
// #ifdef MP
const backupKey = `backup_${backup.info.timestamp}`
storage.set(backupKey, backup)
resolve(backupKey)
// #endif
} catch (e) {
console.error('保存備份失敗:', e)
reject(e)
}
})
}
/**
* 從文件加載備份
* @param {string} filePath 文件路徑
* @returns {Promise} 包含備份數據的Promise
*/
loadBackupFromFile(filePath) {
return new Promise((resolve, reject) => {
try {
// 僅在App平台可用
// #ifdef APP-PLUS
const fs = uni.getFileSystemManager()
fs.readFile({
filePath,
encoding: 'utf8',
success: (res) => {
try {
const backup = JSON.parse(res.data)
console.log('備份文件加載成功')
resolve(backup)
} catch (e) {
console.error('解析備份文件失敗:', e)
reject(e)
}
},
fail: (err) => {
console.error('讀取備份文件失敗:', err)
reject(err)
}
})
// #endif
// 在小程序平台,從本地存儲加載備份
// #ifdef MP
const backup = storage.get(filePath)
if (backup) {
resolve(backup)
} else {
reject(new Error('未找到備份'))
}
// #endif
// H5平台需要用戶手動選擇文件
// #ifdef H5
reject(new Error('H5平台請使用恢復備份方法'))
// #endif
} catch (e) {
console.error('加載備份失敗:', e)
reject(e)
}
})
}
/**
* 恢復備份
* @param {Object} backup 備份數據
* @param {boolean} override 是否覆蓋現有數據
* @returns {boolean} 是否成功
*/
restoreBackup(backup, override = false) {
try {
if (!backup || !backup.data || !backup.info) {
console.error('備份數據無效')
return false
}
const { data } = backup
// 恢復數據
Object.keys(data).forEach(key => {
// 如果不覆蓋且已存在,則跳過
if (!override && storage.has(key)) {
return
}
storage.set(key, data[key])
})
console.log('備份恢復成功')
return true
} catch (e) {
console.error('恢復備份失敗:', e)
return false
}
}
/**
* H5平台從文件選擇器恢復備份
* @returns {Promise} 操作Promise
*/
restoreBackupFromFileSelector() {
return new Promise((resolve, reject) => {
// 僅在H5平台可用
// #ifdef H5
try {
// 創建文件輸入元素
const input = document.createElement('input')
input.type = 'file'
input.accept = 'application/json'
input.onchange = (event) => {
const file = event.target.files[0]
if (!file) {
reject(new Error('未選擇文件'))
return
}
const reader = new FileReader()
reader.onload = (e) => {
try {
const backup = JSON.parse(e.target.result)
// 恢復備份
const result = this.restoreBackup(backup, true)
if (result) {
resolve(backup)
} else {
reject(new Error('恢復備份失敗'))
}
} catch (err) {
console.error('解析備份文件失敗:', err)
reject(err)
}
}
reader.onerror = (err) => {
console.error('讀取備份文件失敗:', err)
reject(err)
}
reader.readAsText(file)
}
// 觸發文件選擇
document.body.appendChild(input)
input.click()
document.body.removeChild(input)
} catch (e) {
console.error('恢復備份失敗:', e)
reject(e)
}
// #endif
// 非H5平台
// #ifndef H5
reject(new Error('此方法僅在H5平台可用'))
// #endif
})
}
/**
* 獲取所有備份列表(小程序平台)
* @returns {Array} 備份列表
*/
getBackupList() {
try {
// 僅在小程序平台可用
// #ifdef MP
const keys = storage.keys()
const backupKeys = keys.filter(key => key.startsWith('backup_'))
const backupList = backupKeys.map(key => {
const backup = storage.get(key)
return {
key,
info: backup ? backup.info : null
}
}).filter(item => item.info)
return backupList
// #endif
// 非小程序平台
// #ifndef MP
return []
// #endif
} catch (e) {
console.error('獲取備份列表失敗:', e)
return []
}
}
}
// 創建數據備份實例
const dataBackup = new DataBackup()
export default dataBackup使用數據備份與恢復:
javascript
import dataBackup from '@/utils/backup'
// 創建並保存備份
function createBackup() {
const backup = dataBackup.createBackup()
if (backup) {
dataBackup.saveBackupToFile(backup)
.then(path => {
uni.showToast({
title: '備份成功',
icon: 'success'
})
console.log('備份路徑:', path)
})
.catch(err => {
uni.showToast({
title: '備份失敗',
icon: 'none'
})
console.error('備份失敗:', err)
})
} else {
uni.showToast({
title: '創建備份失敗',
icon: 'none'
})
}
}
// 恢復備份(H5平台)
function restoreBackup() {
// #ifdef H5
dataBackup.restoreBackupFromFileSelector()
.then(() => {
uni.showToast({
title: '恢復成功',
icon: 'success'
})
})
.catch(err => {
uni.showToast({
title: '恢復失敗',
icon: 'none'
})
console.error('恢復失敗:', err)
})
// #endif
// 小程序平台
// #ifdef MP
const backupList = dataBackup.getBackupList()
if (backupList.length === 0) {
uni.showToast({
title: '沒有可用的備份',
icon: 'none'
})
return
}
// 顯示備份列表
uni.showActionSheet({
itemList: backupList.map(item => {
const date = new Date(item.info.timestamp)
return `${date.toLocaleString()} (${item.info.count}項)`
}),
success: (res) => {
const index = res.tapIndex
const backup = storage.get(backupList[index].key)
if (backup) {
const result = dataBackup.restoreBackup(backup, true)
if (result) {
uni.showToast({
title: '恢復成功',
icon: 'success'
})
} else {
uni.showToast({
title: '恢復失敗',
icon: 'none'
})
}
}
}
})
// #endif
}最佳實踐
性能優化
- 批量操作:盡量減少存儲操作次數,使用批量讀寫
- 延遲寫入:對於頻繁變化的數據,可以使用防抖或節流技術延遲寫入
- 壓縮數據:對於大型數據,可以使用壓縮算法減小體積
- 分片存儲:將大型數據分割成多個小塊存儲,避免超出單個鍵的大小限制
javascript
// 延遲寫入示例
function debounce(func, wait) {
let timeout
return function(...args) {
clearTimeout(timeout)
timeout = setTimeout(() => {
func.apply(this, args)
}, wait)
}
}
// 使用延遲寫入保存用戶設置
const saveSettings = debounce((settings) => {
storage.set('settings', settings)
}, 500)
// 用戶更改設置時調用
function updateSettings(key, value) {
const settings = storage.get('settings', {})
settings[key] = value
saveSettings(settings)
}安全建議
- 敏感數據加密:使用加密存儲敏感信息
- 定期清理:定期清理過期或不必要的數據
- 數據驗證:讀取數據時進行驗證,防止使用損壞的數據
- 錯誤處理:妥善處理存儲操作中的異常
javascript
// 數據驗證示例
function getUserInfo() {
try {
const userInfo = storage.get('userInfo')
// 驗證數據完整性
if (!userInfo || !userInfo.id || !userInfo.name) {
console.warn('用戶信息不完整')
return null
}
return userInfo
} catch (e) {
console.error('獲取用戶信息失敗:', e)
return null
}
}存儲策略
分級存儲:根據數據重要性和使用頻率選擇不同的存儲方式
- 關鍵數據:加密存儲
- 常用數據:本地存儲
- 大型數據:文件存儲或IndexedDB
- 臨時數據:內存緩存
存儲生命週期:為不同類型的數據設置合適的過期時間
- 會話數據:應用關閉時清除
- 緩存數據:設置合理的過期時間
- 用戶數據:長期保存,但提供清除選項
javascript
// 分級存儲示例
class DataManager {
constructor() {
// 內存緩存
this.cache = new Map()
// 本地存儲
this.storage = storage
// 加密存儲
this.secureStorage = cryptoStorage
// 清理過期緩存
setInterval(() => {
this.cleanExpiredCache()
}, 60000) // 每分鐘清理一次
}
// 設置緩存(內存)
setCache(key, data, expires = 60000) {
this.cache.set(key, {
data,
expires: expires ? Date.now() + expires : null
})
}
// 獲取緩存
getCache(key) {
const item = this.cache.get(key)
if (!item) {
return null
}
if (item.expires && item.expires < Date.now()) {
this.cache.delete(key)
return null
}
return item.data
}
// 清理過期緩存
cleanExpiredCache() {
for (const [key, item] of this.cache.entries()) {
if (item.expires && item.expires < Date.now()) {
this.cache.delete(key)
}
}
}
// 設置數據(根據類型選擇存儲方式)
setData(key, data, options = {}) {
const { type = 'normal', expires } = options
switch (type) {
case 'cache':
// 內存緩存
this.setCache(key, data, expires)
break
case 'secure':
// 加密存儲
this.secureStorage.set(key, data, expires)
break
case 'normal':
default:
// 普通存儲
this.storage.set(key, data, expires)
break
}
}
// 獲取數據(按優先級查找)
getData(key, defaultValue = null, type = 'normal') {
// 先查找內存緩存
const cachedData = this.getCache(key)
if (cachedData !== null) {
return cachedData
}
// 根據類型查找存儲
let data = null
switch (type) {
case 'secure':
data = this.secureStorage.get(key, defaultValue)
break
case 'normal':
default:
data = this.storage.get(key, defaultValue)
break
}
// 如果找到數據,更新緩存
if (data !== null && data !== defaultValue) {
this.setCache(key, data)
}
return data
}
}
// 創建數據管理實例
const dataManager = new DataManager()
// 使用示例
dataManager.setData('tempData', { value: 123 }, { type: 'cache', expires: 30000 }) // 30秒過期
dataManager.setData('userData', { name: '張三' }, { type: 'normal' }) // 永不過期
dataManager.setData('password', '123456', { type: 'secure', expires: 7 * 24 * 3600 * 1000 }) // 7天過期
const tempData = dataManager.getData('tempData')
const userData = dataManager.getData('userData')
const password = dataManager.getData('password', null, 'secure')總結
本文詳細介紹了uni-app中的數據存儲方法和最佳實踐,包括:
- 基礎存儲API:uni.setStorage、uni.getStorage等方法的使用
- 存儲限制:不同平台的存儲空間限制及應對策略
- 存儲封裝與管理:封裝存儲管理模塊,支持過期時間、批量操作等
- 加密存儲:敏感數據的加密存儲方案
- 文件存儲:大型數據的文件系統存儲方法
- IndexedDB存儲:H5平台的大容量數據存儲方案
- 數據同步與備份:實現雲端同步和本地備份恢復功能
- 最佳實踐:性能優化、安全建議和存儲策略
通過合理使用這些存儲方法和技巧,可以有效管理uni-app應用中的數據,提高應用性能和用戶體驗。在實際開發中,應根據數據特性和業務需求選擇合適的存儲方案,並注意數據安全和性能優化。