Как сохранить магазин стройный
Существует ли прямая опция сохранения данных хранилища 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:
У меня работает с тонкой версией
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)
});