Как сохранить магазин стройный

Существует ли прямая опция сохранения данных хранилища svelte, чтобы даже при обновлении страницы данные были доступны.

Я не использую локальное хранилище, так как хочу, чтобы значения были реактивными.

15 ответов

Решение

Вы можете вручную создать подписку на свой магазин и сохранить изменения в 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 Kit у меня были проблемы с SSR. Это было мое решение , основанное на стройной Kit FAQ , в ответ по Matyanson и ответ Аднан Y.

В качестве бонуса это решение также обновляет доступный для записи, если localStorageизменения (например, в другой вкладке). Таким образом, это решение работает во всех вкладках. См . Событие Window: storage

Поместите это в файл машинописного текста, например $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: "" })

https://higsch.me/2019/06/22/2019-06-21-svelte-local-storage/ Маттиаса Штала предлагает:

// 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 функция для включения постоянного состояния.

Нашел библиотеку svelte-local-storage-store , которая реализует эту функциональность. Работал на меня.

Пример из их README:

      // in store.ts or similar
import { writable } from 'svelte-local-storage-store'

// First param `preferences` is the local storage key.
// Second param is the initial value.
export const preferences = writable('preferences', {
  theme: 'dark',
  pane: '50%',
  ...
})


// in views

import { get } from 'svelte/store'
import { preferences } from './stores'

preferences.subscribe(...) // subscribe to changes
preferences.update(...) // update value
preferences.set(...) // set value
get(preferences) // read value
$preferences // read value with automatic subscription

TL; DR: вот функция, которая занимается не только установкой и получением, но и удалением.

      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) не установит return null для следующего localStorage.getItem('someval') но "null"что, вероятно, не то, что хотелось бы. Таким образом, это также проверяет наличие undefined и null и соответственно удаляет элемент.

Если кому-то нужно заставить это работать с объектами 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}>

С 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()

Эта функция синхронизирует svelte store с localStorage. Если значение не сохранено, вместо него используется параметр initValue.

Я также добавил машинописный текст.

      import { writable, Writable } from 'svelte/store';

const wStorage = <T>(key: string, initValue: T): Writable<T> => {
    const storedValueStr = localStorage.getItem(key);
    const storedValue: T | null = JSON.parse(storedValueStr);

    const store = writable(storedValue ?? initValue);
    store.subscribe((val) => {
        localStorage.setItem(key, JSON.stringify(val));
    })
    return store;
}

export default wStorage;

Затем вы можете использовать эту функцию в другом месте, как вы привыкли, с writable:

      const count = wStorage('count', 0);

Вы можете также проверить это https://github.com/andsala/svelte-persistent-store

Также, если вы используете sapper и не хотите, чтобы что-то запускалось на сервере, вы можете использовать хук onMount

onMount(() => {
  console.log('I only run in the browser');
});

Вот пример из моей книги, надеюсь, это поможет.

Вы можете сделать это следующим образом:

      import { writable } from 'svelte/store';
import { browser } from '$app/environment';

// check if the item exists in local storage, if so, return the item, otherwise, return null. (This is to avoid errors on initial reads of the store)
// browser && makes sure the command only works in the client side (browser).
const get_local_storage =
    browser && localStorage.getItem('presisted_local_store')
        ? browser && localStorage.getItem('presisted_local_store')
        : null;
// create a writable store
export const presisted_local_store = writable(JSON.parse(get_local_storage));
// create a subscribe method for the store to write back to the local storage (again, on the browser)
presisted_local_store.subscribe((value) => {
    browser && localStorage.setItem('presisted_local_store', JSON.stringify(value));

Надеюсь, это поможет кому-то.

скопировал этот код из одного из моих проектов $lib/savable.ts

      import type { Writable, StartStopNotifier, Unsubscriber } from 'svelte/types/runtime/store';
import { writable } from 'svelte/store';

const attach = (writable: Writable<unknown>, key='store'): void =>{
    const json = localStorage.getItem(key);
    if (json) {
       writable.set(JSON.parse(json));
    }

    writable.subscribe(current => {
        localStorage.setItem(key, JSON.stringify(current));
    });
}
interface Savable<T> extends Writable<T> {
    mount(localstore: Storage): void
    dismount(localstore: Storage): JSON
    unsub: Unsubscriber
}
function savable<T>(key: string, value?: T, start?: StartStopNotifier<T>): Savable<T>{
    const base = writable(value, start)
    return {
        ...base,
        mount(localstore) {
            if(this.mounted) throw new Error("Already mounted");
            this.mounted = true;

            const json = localstore.getItem(key);
            if (json) {
                base.set(JSON.parse(json));
            }

            this.unsub = base.subscribe(current => {
                localStorage.setItem(key, JSON.stringify(current));
            });
            console.log(this)
        },
        dismount(localstore) {
            if(!this.mounted) throw new Error("Not mounted");
            const json = JSON.parse(localstore.getItem(key))
            this.unsub()
            localstore.removeItem(key)
            return json
        },
        unsub() {
           throw new Error('Cannot unsubscribe when not subscribed')
        }
    }
}
export {
    attach,
    savable,
};
export type {
    Savable
}
export default savable

вот пример сохранения, используемого в index.svelte

      <!—- Typescript is not required  —->
<script lang=ts>
    import savable from `$lib/savable`;
    const value = savable(‘input_value’);
    import { onMount } from ‘svelte’;
    onMount(()=>{
        value.mount()
    })
</script>

<input bind:value={$value}></input>

Ниже приведен пример использования Typescript. Этот пример позволяет получить начальное значение , подписаться на будущие обновления и установить значения.

Не забудьте вызвать unsubscribe( ) в onDestroy()

Создайте файл: local-storage.ts

      type Invalidator<T> = (value?: T) => void;

interface WritableLocalStorage<T> {
  readonly value: T;

  subscribe(this: void, run: Subscriber<T>, invalidate?: Invalidator<T>): Unsubscriber;
  set(this: void, value: T): void;
}

export const useLocalStorage = <T>(key: string, initialValue?: T): WritableLocalStorage<T> => {
  const item = localStorage?.getItem(key);
  const value: T = item ? JSON.parse(item) : initialValue;

  const { set, subscribe } = writable(value);

  return {
    value,
    subscribe,
    set: (val) => {
      localStorage?.setItem(key, JSON.stringify(val));
      set(val);
    }
  };
};

Внутри стройного файла:

      const { set, subscribe, value } = useLocalStorage<string[]>('items', []);

// get initial value from localStorage
let items = value;

// subscribe and retrieve future updates
const unsubscribe = subscribe(val => {
  items = val;
});

// set value
set(['item1']);

// unsubscribe when component gets destroyed
onDestroy(() => unsubscribe());

В SvelteKit официальный метод называется Snapshot:

https://kit.svelte.dev/docs/snapshots

У меня работает с тонкой версией 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)
});

Другие вопросы по тегам