← 返回目录

第9课:数据存储

课程概述

本课程将深入讲解UniApp中的数据存储机制,包括本地存储API、数据加密、存储最佳实践以及云开发数据库操作。掌握数据存储技术对于构建功能完整的应用至关重要。

1. 本地存储API

UniApp提供了完整的本地存储API,支持异步和同步两种方式。本地存储数据会永久保存,除非用户手动清除应用数据。

1.1 异步存储API

异步API不会阻塞主线程,适合存储大量数据或在关键路径上使用。

// 存储数据
uni.setStorage({
    key: 'userInfo',
    data: {
        id: 1,
        name: '张三',
        age: 25
    },
    success: function () {
        console.log('数据存储成功')
    },
    fail: function (error) {
        console.error('存储失败', error)
    }
})

// 获取数据
uni.getStorage({
    key: 'userInfo',
    success: function (res) {
        console.log('获取到的数据', res.data)
    },
    fail: function (error) {
        console.error('获取失败', error)
    }
})

// 移除数据
uni.removeStorage({
    key: 'userInfo',
    success: function () {
        console.log('数据移除成功')
    }
})

// 清空所有数据
uni.clearStorage({
    success: function () {
        console.log('所有数据已清空')
    }
})

// 获取存储信息
uni.getStorageInfo({
    success: function (res) {
        console.log('当前存储信息', res)
        // res.keys: 当前storage中所有的key
        // res.currentSize: 当前占用的空间大小, 单位kb
        // res.limitSize: 限制的空间大小, 单位kb
    }
})

1.2 同步存储API

同步API会阻塞主线程,使用时需要注意性能影响,适合存储少量数据或在非关键路径上使用。

// 同步存储数据
try {
    uni.setStorageSync('token', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9')
    console.log('数据存储成功')
} catch (e) {
    console.error('存储失败', e)
}

// 同步获取数据
try {
    const token = uni.getStorageSync('token')
    console.log('获取到的token', token)
} catch (e) {
    console.error('获取失败', e)
}

// 同步移除数据
try {
    uni.removeStorageSync('token')
    console.log('数据移除成功')
} catch (e) {
    console.error('移除失败', e)
}

// 同步清空所有数据
try {
    uni.clearStorageSync()
    console.log('所有数据已清空')
} catch (e) {
    console.error('清空失败', e)
}

// 同步获取存储信息
try {
    const info = uni.getStorageInfoSync()
    console.log('当前存储信息', info)
} catch (e) {
    console.error('获取信息失败', e)
}
提示: 异步API和同步API的选择建议:

2. 存储数据类型

UniApp的本地存储支持多种数据类型,但实际存储时都会被转换为字符串。

// 存储字符串
uni.setStorageSync('username', 'admin')

// 存储数字
uni.setStorageSync('age', 25)

// 存储布尔值
uni.setStorageSync('isLogin', true)

// 存储对象(会被自动JSON序列化)
uni.setStorageSync('user', {
    id: 1,
    name: '张三',
    avatar: 'https://example.com/avatar.jpg'
})

// 存储数组
uni.setStorageSync('cart', [
    { id: 1, name: '商品1', price: 99 },
    { id: 2, name: '商品2', price: 199 }
])

// 存储null和undefined
uni.setStorageSync('empty', null)
uni.setStorageSync('undefined', undefined)
注意: 虽然可以存储各种类型的数据,但存储大小有限制:

3. 数据加密存储

对于敏感数据(如用户token、个人信息等),建议进行加密存储,提高数据安全性。

// utils/crypto.js
import CryptoJS from 'crypto-js'

const SECRET_KEY = 'your-secret-key-32-characters'

export function encrypt(data) {
    const jsonString = JSON.stringify(data)
    const encrypted = CryptoJS.AES.encrypt(jsonString, SECRET_KEY).toString()
    return encrypted
}

export function decrypt(encryptedData) {
    try {
        const bytes = CryptoJS.AES.decrypt(encryptedData, SECRET_KEY)
        const decryptedString = bytes.toString(CryptoJS.enc.Utf8)
        return JSON.parse(decryptedString)
    } catch (e) {
        console.error('解密失败', e)
        return null
    }
}

export function setSecureStorage(key, data) {
    const encrypted = encrypt(data)
    uni.setStorageSync(key, encrypted)
}

export function getSecureStorage(key) {
    const encrypted = uni.getStorageSync(key)
    if (!encrypted) return null
    return decrypt(encrypted)
}

export function removeSecureStorage(key) {
    uni.removeStorageSync(key)
}
// 使用加密存储
import { setSecureStorage, getSecureStorage, removeSecureStorage } from '@/utils/crypto'

// 存储敏感数据
setSecureStorage('userInfo', {
    id: 1,
    name: '张三',
    phone: '13800138000',
    idCard: '110101199001011234'
})

// 获取敏感数据
const userInfo = getSecureStorage('userInfo')
console.log(userInfo)

// 移除敏感数据
removeSecureStorage('userInfo')

4. 存储封装工具

为了方便使用,可以封装一个统一的存储工具类。

// utils/storage.js
class Storage {
    constructor(prefix = 'app_') {
        this.prefix = prefix
    }

    getKey(key) {
        return this.prefix + key
    }

    set(key, value, expire = null) {
        const data = {
            value: value,
            time: Date.now()
        }
        if (expire) {
            data.expire = Date.now() + expire * 1000
        }
        uni.setStorageSync(this.getKey(key), data)
    }

    get(key) {
        const data = uni.getStorageSync(this.getKey(key))
        if (!data) return null
        
        if (data.expire && Date.now() > data.expire) {
            this.remove(key)
            return null
        }
        
        return data.value
    }

    remove(key) {
        uni.removeStorageSync(this.getKey(key))
    }

    clear() {
        const info = uni.getStorageInfoSync()
        info.keys.forEach(key => {
            if (key.startsWith(this.prefix)) {
                uni.removeStorageSync(key)
            }
        })
    }

    setWithExpire(key, value, seconds) {
        this.set(key, value, seconds)
    }

    has(key) {
        return uni.getStorageSync(this.getKey(key)) !== ''
    }

    getAll() {
        const info = uni.getStorageInfoSync()
        const result = {}
        info.keys.forEach(key => {
            if (key.startsWith(this.prefix)) {
                const originalKey = key.replace(this.prefix, '')
                result[originalKey] = this.get(originalKey)
            }
        })
        return result
    }
}

export default new Storage('uniapp_')
// 使用封装的存储工具
import storage from '@/utils/storage'

// 存储数据
storage.set('username', 'admin')
storage.set('userInfo', { id: 1, name: '张三' })

// 存储带过期时间的数据(10秒后过期)
storage.setWithExpire('tempData', { code: '123456' }, 10)

// 获取数据
const username = storage.get('username')
const userInfo = storage.get('userInfo')

// 检查数据是否存在
if (storage.has('userInfo')) {
    console.log('用户信息存在')
}

// 移除数据
storage.remove('username')

// 获取所有数据
const allData = storage.getAll()

// 清空所有数据
storage.clear()

5. 云开发数据库

如果使用UniApp云开发,可以使用云数据库进行数据存储。云数据库支持丰富的查询操作和实时数据同步。

5.1 数据库初始化

// 初始化云开发
const db = uniCloud.database()

// 获取集合引用
const usersCollection = db.collection('users')
const ordersCollection = db.collection('orders')

5.2 基本CRUD操作

// 添加数据
async function addUser() {
    try {
        const res = await usersCollection.add({
            name: '张三',
            age: 25,
            email: 'zhangsan@example.com',
            createTime: db.serverDate()
        })
        console.log('添加成功', res.id)
    } catch (e) {
        console.error('添加失败', e)
    }
}

// 查询数据
async function getUsers() {
    try {
        const res = await usersCollection.where({
            age: db.command.gte(18)
        }).get()
        console.log('查询结果', res.data)
    } catch (e) {
        console.error('查询失败', e)
    }
}

// 更新数据
async function updateUser(userId) {
    try {
        const res = await usersCollection.doc(userId).update({
            age: 26,
            updateTime: db.serverDate()
        })
        console.log('更新成功', res.updated)
    } catch (e) {
        console.error('更新失败', e)
    }
}

// 删除数据
async function deleteUser(userId) {
    try {
        const res = await usersCollection.doc(userId).remove()
        console.log('删除成功', res.deleted)
    } catch (e) {
        console.error('删除失败', e)
    }
}

5.3 高级查询

// 条件查询
const res = await usersCollection.where({
    age: db.command.gte(18).and(db.command.lte(30)),
    name: db.command.eq('张三')
}).get()

// 模糊查询
const res = await usersCollection.where({
    name: db.command.like('张%')
}).get()

// 排序
const res = await usersCollection.where({
    age: db.command.gte(18)
}).orderBy('createTime', 'desc')
 .limit(10)
 .skip(0)
 .get()

// 字段过滤
const res = await usersCollection.field({
    name: true,
    age: true,
    _id: true
}).get()

// 统计查询
const res = await usersCollection.where({
    age: db.command.gte(18)
}).count()
console.log('符合条件的用户数', res.total)

6. 实际应用案例

6.1 用户登录状态管理

// store/user.js
import storage from '@/utils/storage'

const USER_KEY = 'user_info'
const TOKEN_KEY = 'access_token'

export default {
    namespaced: true,
    
    state: {
        userInfo: null,
        token: null
    },
    
    mutations: {
        SET_USER(state, userInfo) {
            state.userInfo = userInfo
            storage.set(USER_KEY, userInfo)
        },
        
        SET_TOKEN(state, token) {
            state.token = token
            storage.set(TOKEN_KEY, token)
        },
        
        CLEAR_USER(state) {
            state.userInfo = null
            state.token = null
            storage.remove(USER_KEY)
            storage.remove(TOKEN_KEY)
        }
    },
    
    actions: {
        init({ commit }) {
            const userInfo = storage.get(USER_KEY)
            const token = storage.get(TOKEN_KEY)
            
            if (userInfo && token) {
                commit('SET_USER', userInfo)
                commit('SET_TOKEN', token)
            }
        },
        
        login({ commit }, { userInfo, token }) {
            commit('SET_USER', userInfo)
            commit('SET_TOKEN', token)
        },
        
        logout({ commit }) {
            commit('CLEAR_USER')
        }
    },
    
    getters: {
        isLogin: state => !!state.token,
        userId: state => state.userInfo?.id,
        userName: state => state.userInfo?.name
    }
}

6.2 购物车数据管理

// utils/cart.js
import storage from '@/utils/storage'

const CART_KEY = 'shopping_cart'

export default {
    getCart() {
        return storage.get(CART_KEY) || []
    },
    
    addToCart(product) {
        const cart = this.getCart()
        const existingItem = cart.find(item => item.id === product.id)
        
        if (existingItem) {
            existingItem.quantity += product.quantity || 1
        } else {
            cart.push({
                id: product.id,
                name: product.name,
                price: product.price,
                image: product.image,
                quantity: product.quantity || 1
            })
        }
        
        storage.set(CART_KEY, cart)
        return cart
    },
    
    removeFromCart(productId) {
        let cart = this.getCart()
        cart = cart.filter(item => item.id !== productId)
        storage.set(CART_KEY, cart)
        return cart
    },
    
    updateQuantity(productId, quantity) {
        const cart = this.getCart()
        const item = cart.find(item => item.id === productId)
        
        if (item) {
            item.quantity = quantity
            if (item.quantity <= 0) {
                return this.removeFromCart(productId)
            }
            storage.set(CART_KEY, cart)
        }
        
        return cart
    },
    
    clearCart() {
        storage.remove(CART_KEY)
        return []
    },
    
    getCartTotal() {
        const cart = this.getCart()
        return cart.reduce((total, item) => {
            return total + item.price * item.quantity
        }, 0)
    },
    
    getCartCount() {
        const cart = this.getCart()
        return cart.reduce((count, item) => {
            return count + item.quantity
        }, 0)
    }
}

6.3 搜索历史记录

// utils/search.js
import storage from '@/utils/storage'

const HISTORY_KEY = 'search_history'
const MAX_HISTORY = 10

export default {
    getHistory() {
        return storage.get(HISTORY_KEY) || []
    },
    
    addHistory(keyword) {
        if (!keyword || keyword.trim() === '') return
        
        let history = this.getHistory()
        
        history = history.filter(item => item !== keyword)
        history.unshift(keyword)
        
        if (history.length > MAX_HISTORY) {
            history = history.slice(0, MAX_HISTORY)
        }
        
        storage.set(HISTORY_KEY, history)
        return history
    },
    
    removeHistory(keyword) {
        let history = this.getHistory()
        history = history.filter(item => item !== keyword)
        storage.set(HISTORY_KEY, history)
        return history
    },
    
    clearHistory() {
        storage.remove(HISTORY_KEY)
        return []
    }
}

7. 存储最佳实践

最佳实践建议:

8. 常见问题

Q1: 存储的数据在应用更新后还在吗?

A: 本地存储的数据在应用更新后仍然存在,除非用户手动清除应用数据或卸载应用。

Q2: 如何处理存储空间不足的情况?

A: 可以通过uni.getStorageInfo获取存储信息,当空间不足时提示用户清理数据或使用云存储。

Q3: 本地存储和云数据库如何选择?

A: 本地存储适合存储用户配置、缓存数据等;云数据库适合存储需要同步、共享的业务数据。

Q4: 如何实现数据同步?

A: 可以结合本地存储和云数据库,本地作为缓存,云端作为数据源,定期同步数据。

9. 课程总结

本课程详细讲解了UniApp中的数据存储机制,包括:

掌握数据存储技术后,你可以在应用中实现用户状态管理、数据缓存、离线功能等重要特性。下一课我们将学习组件通信的相关知识。