- Published on
Zustand + IndexedDB:打造高性能、可离线的本地状态管理
- Authors

- Name
- Monster Cone
为了摆脱 Microsoft To Do 每次使用都需要修改密码登录的困扰,我决定开发一个无需登录、可离线使用的 Monster TodoList。本文将分享如何利用 Zustand 和 IndexedDB 实现高效的离线数据持久化。
1. Zustand 持久化基础
要创建一个可持久化的 Store,我们需要使用persist、createJSONStorage这两个中间件。
让我们从一个简单的 useCountStore 示例开始:
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'
interface CountState {
count: number
add: () => void
sub: () => void
}
const useCountStore = create<CountState>()(
persist(
(set) => ({
count: 0,
add: () => set((state) => ({ count: state.count + 1 })),
sub: () => set((state) => ({ count: state.count - 1 })),
}),
{
name: 'count', // 存储在 Storage 中的键名
storage: createJSONStorage(() => sessionStorage), // 使用 sessionStorage 作为存储介质
}
)
)
persist接受两个参数:
- Store 定义: 即
(set, get) => ({ ... }),定义了 Store 的初始状态和 actions。 - 持久化配置对象: Store持久化的详细配置。
关键持久化配置如下:
- name: 存在Storage中的键名。
- storage: 使用createJSONStorage中间件返回一个包含getItem、setItem、removeItem方法的Storage接口对象,默认为localStorage。
- partialize: 可选函数,返回要持久化的键值对对象,countStore 如果还有其他state,但我只想持久化count,则返回
{count: state.count}, 不会持久化方法。 - version: 持久化版本号,用于后面migrate时从旧版本迁移到新版本。
- migrate: 一个函数,在读取到的持久化版本和当前Store版本不一样时触发,根据版本号升级兼容数据。
还有一些其他配置可查看Zustand 官方文档 Persisting store data
2. 理解自定义持久化
在第一节我们已经知道如何配置持久化,接下来我们要自定义持久化。
const customStorage = {
getItem: async (name: string) => { return JSON.stringify(state: {count: 0})},
setItem: async (name: string, value: string) => {},
removeItem: async (name: string) => {},
}
上面就是自定义持久化的基础模板。
- getItem: 当我们在页面中使用useCountStore时,会调用该方法,它接受一个name参数,为持久化配置的key。它需要返回一个包含state属性的字符串对象。
- setItem: 当我们修改state时会调用该方法,它有两个参数,一个持久化配置的Storage键名,一个包含state、version属性的字符串对象。可自定义数据存放在哪里。
- removeItem: 通过useCountStore.persist.clearStorage()调用。
3. 自定义持久化初始化Store
上述我们已经知道了自定义持久化配置,只需要添加相关的indexedDB的逻辑即可。
建议仅在getItem中获取indexedDB数据初始化store即可,使用indexedDB存储的有序数据不适合通过setItem持久化,建议直接操作indexedDB。
getItem中可以执行异步操作,可以发送请求获取远程数据进行初始化。