Stores
有時候你會想要在不同的 component 之間共享一個值,這時候你可以使用 store。
Writeable Store
在 Svelte 中,store 是一個簡單的物件,並擁有一個訂閱 subscribe 的方法,當 store 的值改變時,所有訂閱它的 component 都會被通知到。
首先新增一個 stores.js 檔案,並且在裡面建立一個 store。
// stores.js
import { writable } from "svelte/store";
export const count = writable(0);
接下來建立一個幫 count +1 的 component。
<!-- Increment.svelte -->
<script>
import { count } from './stores.js';
function increment() {
count.update((n) => n + 1);
}
</script>
<button on:click={increment}>
+
</button>
建立一個幫 count -1 的 component。
<!-- Decrement.svelte -->
<script>
import { count } from './stores.js';
function decrement() {
count.update((n) => n - 1);
}
</script>
<button on:click={decrement}>
-
</button>
接著在 App.svelte 中引入 increment 與 decrement 的 component,並顯示 count 的值。
<script>
import { count } from './stores.js';
import Increment from './Increment.svelte';
import Decrement from './Decrement.svelte';
let count_value;
count.subscribe(value => {
count_value = value;
});
</script>
<h1>The count is {count_value}</h1>
<Increment />
<Decrement />
Auto-Subscriptions
上面的例子有個潛在的問題,如果在 component 被銷毀之前沒有取消訂閱,那麼就會導致 memory leak。
我們可以使用 onDestroy 來取消訂閱。
<script>
import { onDestroy } from 'svelte';
import { count } from './stores.js';
// ...
let count_value;
const unsubscribe = count.subscribe(value => {
count_value = value;
});
onDestroy(unsubscribe);
</script>
呼叫 subscribe 方法會回傳一個 unsubscribe 方法,可以用來取消訂閱。
每次取消訂閱都要呼叫 unsubscribe 這個方法,這樣做很麻煩,所以 Svelte 提供了 auto-subscriptions 的功能,只要在 subscribe 前面加上 $ 符號,就可以自動取消訂閱。
<script>
import { count } from './stores.js';
// ...
</script>
<h1>The count is {$count}</h1>
<!-- ... -->
Svelte 會禁止使用者在命名變數時使用
$符號當作前綴。
Readable Store
除了 writable store 之外,Svelte 還提供了 readable store,readable store 只能被訂閱,不能被更新。
例如顯示時間的 store,通常是不允許被修改的。
// stores.js
import { readable } from "svelte/store";
// new Date() is the initial value, you can set it null or undefined
// the start function is called when the store get its first subscriber
export const time = readable(new Date(), function start(set) {
const interval = setInterval(() => {
set(new Date());
}, 1000);
// the stop function is called when the last subscriber unsubscribes
return function stop() {
clearInterval(interval);
};
});
<script>
import { time } from './stores.js';
</script>
<h1>The time is {$time.toLocaleTimeString()}</h1>
Derived Store
你可以根據其他 store 的值來計算出一個新的 store,這種 store 叫做 derived store。
// stores.js
import { readable, derived } from "svelte/store";
export const time = readable(new Date(), function start(set) {
const interval = setInterval(() => {
set(new Date());
}, 1000);
return function stop() {
clearInterval(interval);
};
});
const start = new Date();
// create a new store that is derived from the time store
export const elapsed = derived(time, ($time) =>
Math.round(($time - start) / 1000)
);
Custom Stores
只要物件有 subscribe 方法,就可以被當作 store 使用。 除此之外,你也可以在物件中加入你自己的方法。
// stores.js
import { writable } from 'svelte/store';
function createCount() {
const = { subscribe, set, update } = writable(0);
return {
subscribe,
increment: () => update(n => n + 1),
decrement: () => update(n => n - 1),
reset: () => set(0)
};
}
export const count = createCount();
<script>
import { count } from './stores.js';
</script>
<h1>The count is {$count}</h1>
<button on:click={count.increment}>+</button>
<button on:click={count.decrement}>-</button>
<button on:click={count.reset}>reset</button>
Store Binding
你可以將 store 綁定到 DOM 元素上,這樣當 store 的值改變時,DOM 元素的值也會跟著改變,反之亦然。
<script>
import { name } from './stores.js';
</script>
<h1>Hello {$name}!</h1>
<input bind:value={$name}>
<button on:click={() => $name += '!'}>
Add exclamation mark!
</button>