【问题标题】:How to persist svelte store如何坚持苗条的商店
【发布时间】:2019-10-22 14:23:58
【问题描述】:

是否有任何直接的选项来持久化 svelte store 数据,这样即使页面被刷新,数据也将可用。

我没有使用本地存储,因为我希望这些值具有反应性。

【问题讨论】:

    标签: svelte 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() 放在onMountif (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() 放入 onMountif (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);
        

        编辑:如果您在应用中使用 SSR 并且不想使用 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.38svelte-kitSapper 的继任者),我使用:

        <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} />
        

        localStorageonMount() 之外不可用

        【讨论】:

          【解决方案8】:

          对于 Svelte Kit,我遇到了 SSR 问题。 这是我基于Svelte Kit FAQanswer by Matyansonanswer 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)
          });
          
          

          【讨论】:

            猜你喜欢
            • 2021-05-11
            • 2020-06-10
            • 2023-02-21
            • 2021-03-30
            • 2020-09-21
            • 2021-07-21
            • 1970-01-01
            • 2021-06-04
            • 2022-11-11
            相关资源
            最近更新 更多