如何坚持苗条的商店
Posted
技术标签:
【中文标题】如何坚持苗条的商店【英文标题】:How to persist svelte store 【发布时间】:2019-10-22 14:23:58 【问题描述】:是否有任何直接的选项来持久化 svelte store 数据,这样即使页面被刷新,数据也将可用。
我没有使用本地存储,因为我希望这些值具有反应性。
【问题讨论】:
【参考方案1】:您可以手动创建对商店的订阅并将更改保存到 localStorage,还可以使用 localStorage 中的潜在值作为默认值。
示例
<script>
import writable from "svelte/store";
const store = writable(localStorage.getItem("store") || "");
store.subscribe(val => localStorage.setItem("store", val));
</script>
<input bind:value=$store />
【讨论】:
这在 svelte 中可以正常工作。在 Sapper 中使用它的推荐方法是什么?我创建了一个单独的 JS 文件,如下所示 import writable, derived from 'svelte/store';导出常量名称 = 可写(localStorage.getItem("store") ||'world'); name.subscribe(val => localStorage.setItem("store", val));但这没有在 sapper 中运行,因为 localStorage 在服务器中不可用 @AnilSivadas 在服务器上做这件事有点复杂。您可以在服务器上跳过它,然后在使用 localStorage 之前在浏览器中使用typeof window !== 'undefined'
检查来完成它。
here 描述了一个类似/相同的示例,包括使用#if process.browser
的解决方案(类似于@Tholle 描述的)。
另一个有趣的选择是使用derived()
,但这会使您拥有双倍的商店数量,这通常是不必要的。【参考方案2】:
你可能还想看看这个https://github.com/andsala/svelte-persistent-store
另外,如果你使用 sapper 并且不想在服务器上运行某些东西,你可以使用 onMount 钩子
onMount(() =>
console.log('I only run in the browser');
);
【讨论】:
【参考方案3】:来自https://github.com/higsch/higsch.me/blob/master/content/post/2019-06-21-svelte-local-storage.md@Matthias Stahl:
假设我们有一个名为 count
的存储变量。
// store.js import writable from 'svelte/store'; export const count = writable(0); // App.svelte import count from 'store.js';
为了使存储持久化,只需将函数
useLocalStorage
包含到store
对象中即可。// store.js import writable from 'svelte/store'; const createWritableStore = (key, startValue) => const subscribe, set = writable(startValue); return subscribe, set, useLocalStorage: () => const json = localStorage.getItem(key); if (json) set(JSON.parse(json)); subscribe(current => localStorage.setItem(key, JSON.stringify(current)); ); ; export const count = createWritableStore('count', 0); // App.svelte import count from 'store.js'; count.useLocalStorage();
然后,在您的
App.svelte
中调用useLocalStorage
函数以启用持久状态。
这在Routify 中非常适合我。对于Sapper,JHeth 建议“只需将count.useLocalStorage()
放在onMount
或if (process.browser)
中使用商店的组件中。”
【讨论】:
对于看到这篇文章并寻找来源的其他人:该博客似乎不再存在,只是 github 上的来源:https://github.com/higsch/higsch.me/blob/master/content/post/2019-06-21-svelte-local-storage.md。但是@mic 已经在这里发布了整个代码。另请注意,如果您使用 sapper,则需要注意它是在服务器还是浏览器上运行。 要使其在 Sapper 中正常工作,只需将count.useLocalStorage()
放入 onMount
或 if (process.browser)
使用商店的组件中。【参考方案4】:
如果有人需要使用 javascript 对象来实现这一点:
export const stored_object = writable(
localStorage.stored_object? JSON.parse(localStorage.stored_object) : );
stored_object.subscribe(val => localStorage.setItem("stored_object",JSON.stringify(val)));
好处是您可以使用 $ 简写访问可写对象,例如
<input type="text" bind:value=$stored_object.name>
<input type="text" bind:value=$stored_object.price>
【讨论】:
【参考方案5】:TLDR:这是一个不仅负责设置和获取,还负责删除的函数。
function persistent(name)
const value = writable(localStorage.getItem(name));
value.subscribe(val => [null, undefined].includes(val) ? localStorage.removeItem(name) : localStorage.setItem(name, val));
return value;
export const my_token = persistent('token');
推理:与直觉相反,localStorage.setItem('someval', null)
不会为下一个localStorage.getItem('someval')
设置 return null 但"null"
这可能不是人们想要的。因此,这也会检查 undefined 和 null 并相应地删除该项目。
【讨论】:
我真的很喜欢设置为 null 时删除 localStorage 中的值的概念。我看到了如何使用导出的my_token.set("hello")
,但不清楚如何使用该函数来get
my_token.js 存储函数中的值。我可以在浏览器开发工具 --> 应用程序 --> 本地存储屏幕中看到值“hello”,但是您的话是 这是一个不仅负责设置和获取,还负责删除的函数。 我只是不明白get()
在这里是如何工作的。注意:my_token.set(null);
可以很好地删除 LocalStorage 中的值。 .get()
在哪里
哎呀。 import get from "svelte/store";
如果我提议对您的代码进行编辑以显示它正在使用中,您会感到被冒犯吗?【参考方案6】:
此函数将 svelte store 与 localStorage 同步。如果没有存储值,则改为使用 initValue 参数。
我还添加了 Typescript。
import writable, Writable from 'svelte/store';
const wStorage = <T>(key: string, initValue: T): Writable<T> =>
const storedValueStr = localStorage.getItem(key);
const storedValue: T = JSON.parse(storedValueStr);
const store = writable(storedValueStr != null ? storedValue : initValue);
store.subscribe((val) =>
localStorage.setItem(key, JSON.stringify(val));
)
return store;
export default wStorage;
然后您可以在其他地方使用该功能,就像您习惯使用 writable
一样:
const count = wStorage<number>('count', 0);
编辑:如果您在应用中使用 s-s-r 并且不想使用 onMount
或检查 if (process.browser)
的每个可写方法。这是修改后的版本:
const wStorage = <T>(key: string, initValue: T): Writable<T> =>
const store = writable(initValue);
if (typeof Storage === 'undefined') return store;
const storedValueStr = localStorage.getItem(key);
if (storedValueStr != null) store.set(JSON.parse(storedValueStr));
store.subscribe((val) =>
localStorage.setItem(key, JSON.stringify(val));
)
return store;
【讨论】:
这不会导致内存泄漏吗?订阅永远不会退订 @Jahir localStorage 中保存的数据不会被删除,但也不会保存更多数据。只会保存您在应用程序中指定的固定数量的值,不会随着时间的推移累积更多数据。与键配对的值将被覆盖,而不是添加。 我明白这一点。但我的问题是,显式订阅永远不会取消订阅。那么,是否存在订阅永远不会被释放并导致内存泄漏的风险? @Jahir 这取决于您调用wStorage
函数的位置。你调用它多少次,那多少次就是订阅初始化。我在src/store.ts
文件中使用wStorage
,就像在docs 中一样。我相信代码只在那里运行一次,我错过了什么吗?如果您在组件中调用wStorage
函数,请随意修改它(例如返回[store, unsubscribe]
,然后在组件中使用onDestroy(unsubscribe);
)。
@Jahir 当您使用 Writable 创建商店时,svelte 会为您处理订阅/取消订阅 - 您只需在 svelte 文件中引用商店时在商店前面加上 $。【参考方案7】:
使用 svelte 3.38 和 svelte-kit(Sapper 的继任者),我使用:
<script>
import onMount from 'svelte';
import writable from "svelte/store";
let value;
onMount(() =>
value = writable(localStorage.getItem("storedValue") || "defaut value");
value.subscribe(val => localStorage.setItem("storedValue", val));
)
</script>
<input bind:value=$value />
localStorage
在onMount()
之外不可用
【讨论】:
【参考方案8】:对于 Svelte Kit,我遇到了 s-s-r 问题。 这是我基于Svelte Kit FAQ、answer by Matyanson 和answer by Adnan Y 的解决方案。
作为奖励,如果localStorage
更改(例如在不同的选项卡中),此解决方案还会更新可写文件。所以这个解决方案可以跨标签工作。见Window: storage event
将其放入打字稿文件中,例如$lib/store.ts
:
import browser from '$app/env';
import type Writable from 'svelte/store';
import writable, get from 'svelte/store'
const storage = <T>(key: string, initValue: T): Writable<T> =>
const store = writable(initValue);
if (!browser) return store;
const storedValueStr = localStorage.getItem(key);
if (storedValueStr != null) store.set(JSON.parse(storedValueStr));
store.subscribe((val) =>
if ([null, undefined].includes(val))
localStorage.removeItem(key)
else
localStorage.setItem(key, JSON.stringify(val))
)
window.addEventListener('storage', () =>
const storedValueStr = localStorage.getItem(key);
if (storedValueStr == null) return;
const localValue: T = JSON.parse(storedValueStr)
if (localValue !== get(store)) store.set(localValue);
);
return store;
export default storage
可以这样使用:
import storage from '$lib/store'
interface Auth
jwt: string
export const auth = storage<Auth>("auth", jwt: "" )
【讨论】:
工作就像魔术 =) 感谢您的完整代码。只是想知道为什么需要声明if (storedValueStr == null) return;
?因为当 storage
事件监听器运行时,这个键应该已经存在于 localStorage 中了。
@Ammar 我确实遇到过这种情况。所以似乎存在不存在的情况。
[null, undefined].includes(val)
不是严格等同于val == null
吗? (我稍后看到与 null
的松散比较,所以只是想知道是否可以在不改变行为的情况下重写它以保持一致性。)【参考方案9】:
适用于我的苗条版本3.44.1
。
src/store.js 文件:
import writable from "svelte/store";
import browser from "$app/env"
export const fontSize = writable(browser && localStorage.getItem("fontSize") || "15");
fontSize.subscribe((value) =>
if (browser) return localStorage.setItem("fontSize", value)
);
【讨论】:
以上是关于如何坚持苗条的商店的主要内容,如果未能解决你的问题,请参考以下文章