最近在给 Pinia 实现持久化时遇到了一个问题:
ini
const userStore = useUserStore();
console.log(userStore.token);
明明数据已经保存到 localForage 中,但页面首次加载时拿到的却是默认值。
而在异步代码中:
scss
await nextTick();
console.log(userStore.token);
或者:
javascript
setTimeout(() => {
console.log(userStore.token);
}, 100);
却能够正常获取到持久化后的数据。
问题原因
问题的根源在于:
Pinia 的初始化是同步的,而 localForage 的数据读取是异步的。
当执行:
ini
const userStore = useUserStore();
时,Pinia 会立即创建 Store:
scss
创建 Store
↓
执行 state()
↓
返回 Store
此时 Store 中的数据还是默认值:
css
{
token: ""
}
而 localForage 底层使用的是 IndexedDB,读取数据需要异步执行:
ini
const cache = await localforage.getItem("user");
因此会出现下面的执行顺序:
perl
创建 Store
↓
返回默认 state
↓
页面开始渲染
↓
localForage 开始读取缓存
↓
缓存读取完成
↓
store.$patch(cache)
也就是说:
Store 已创建
≠
持久化数据已恢复
所以页面初始化时拿到的是默认值,而不是缓存中的值。
为什么 localStorage 没有这个问题?
因为 localStorage 是同步 API:
ini
const cache = localStorage.getItem("user");
执行流程:
读取缓存
↓
恢复数据
↓
返回 Store
Store 返回时数据已经恢复完成,因此组件中能够直接获取到最新状态。
一个常见误区
很多人会这样写:
ini
pinia.use(async ({ store }) => {
const cache = await localforage.getItem(store.$id);
if (cache) {
store.$patch(cache);
}
});
以为加了 async/await 后 Pinia 会等待数据恢复。
实际上并不会。
Pinia 插件本身是同步执行的,Store 不会等待异步任务结束才创建完成。
所以即使使用:
dart
pinia.use(async () => {})
也无法阻止 Store 先返回。
总结
这个问题本质上不是 Pinia 的问题,也不是 localForage 的问题,而是同步初始化与异步恢复之间的时间差导致的。
Pinia Store 创建:同步
localForage 数据读取:异步
因此当页面首次渲染时:
Store 已存在
数据未恢复
而等异步读取完成后:
Store 已存在
数据已恢复
这就是为什么在页面初始化阶段拿不到值,而在异步代码中却能够正常获取到值的原因。
本文部分内容借助 AI 辅助生成,并由作者整理审核。