經過昨天的介紹,我們大概對 Svelte 所提供的 Store 物件有了一些基礎的認識,原本抽象的 Store 其面貌似乎也漸漸明朗起來。那就讓我們順著昨天文章的脈絡繼續討論 Store 的相關應用吧。
昨天介紹了 writable
Store 的 subscribe
跟 set
這兩種方法。其中 subscribe
這個方法會註冊一個回呼函式(callback function),每當 Store 當中的變數發生改變,就執行這個回呼函式,利用這個特性,我們可以將 Svelte 元件內的變數跟一般 Javscript檔案內的變數做到資料連動的表現。另一個方法 set
則比較簡單,藉由這個方法我們可以直接將 Store 當中的變數更新成指定的值。
今天則是要來介紹第三個方法 update
。顧名思義,update
跟 set
一樣,想必都可以用來更新 Store 當中的變數。跟 set
直接將變數更新成指定的值不同,update
是從現有的值出發,更新成一個相對應的值。
舉例來說,如果我們需要將 Store 當中的變數更新 0
,也就是特定的一個值,這個動作可以藉由 set
完成。然而,如果我們想要將 Store 當中的變數增加一,更新成比現在的值還要多一的數字,也就是 n + 1
,這個時候就需要用 update
了。
讓我們實際在 Svelte 的 REPL (Read-Eval-Print-Loop) 介面練習一下,看一看程式碼該怎麼寫:
/src/store.js
import { writable } from 'svelte/store';
export const stateStore = writable(0);
export const stateStore = writable(0);
0
初始化一個 writable
的 Store。 先在 Javascript 檔案當中做好 writable
Store 之後,接著來到 Svelte 元件來使用這個 Store 物件。
/src/App.svelte
<script>
import { onMount } from 'svelte';
import { stateStore } from './store.js';
let value;
stateStore.subscribe((state) => value = state);
onMount(() => {
const interval = setInterval(() => stateStore.update((state) => state + 1), 1000);
return () => clearInterval(interval)
})
</script>
<h1>Now is {value}!</h1>
第三行:import { stateStore } from './store.js';
引入我們的 Store 物件 stateStore
。
第五行:let value;
在 Svelte 元件當中宣告一個變數 value
。
第七行:stateStore.subscribe((state) => value = state);
用 subscribe
的方法將 Store 當中的變數跟 Svelte 元件中的變數作連動。
第十行:const interval = setInterval(() => stateStore.update((state) => state + 1), 1000);
利用 update
來更新 Store 當中的變數。update
需要一個用來表明該如何更新變數的函式作為參數。以這個例子來說,就是 (state) => state + 1
。這個函式的參數表示現在 Store 當中的變數,並且回傳一個值,做為更新過後的變數。也就是說,透過 setInterval(() => stateStore.update((state) => state + 1), 1000)
,我們就會把 stateStore
這個 Store 當中的變數一秒一秒的往上加。
圖一、一秒一秒往上加
我們上面練習的程式碼看起來挺成功的,透過 Store,成功讓 Javascript 的變數跟 Svelte 元件當中的變數連接在一起。唯一有一個小小的問題,那就是我們利用 subscribe
方法讓兩個變數連接在一起,卻沒有宣告解除連接的方法。當 Svelte 元件從 DOM 當中卸載之後,Svelte 元件中的變數就是一個沒有效果不被需要的變數了,可是 Javascript 當中的變數卻還是依然試圖跟已經卸載的 Svelte 元件中的變數做連動,這麼一來就會浪費記憶體資源。
為了彌補這個錯誤,讓我們修改一下 Svelte 元件 App.svelte
當中的程式碼:
/src/App.svelte
<script>
import { onMount, onDestroy } from 'svelte';
import { stateStore } from './store.js';
let value;
const unsubscribe = stateStore.subscribe((state) => value = state);
onMount(() => {
const interval = setInterval(() => stateStore.update((state) => state + 1), 1000);
return () => clearInterval(interval)
})
onDestroy(unsubscribe);
</script>
<h1>Now is {value}!</h1>
第二行:import { onMount, onDestroy } from 'svelte';
除了 onMount
之外,也讓我們引入 onDestroy
。顧名思義,這也是一個 Svelte 提供的生命週期函式,會在 Svelte 元件被卸載時執行。
第七行:const unsubscribe = stateStore.subscribe((state) => value = state);
在執行 subscribe
這個方法的同時,其實會得到一個回傳函式。如果呼叫這個回傳函式,就會終止 subscribe
的行為,而我們把這個可以終止 subscribe
行為的函式放在 unsubscribe
裡面。按照這一段程式碼具體解釋一次,當我們呼叫 subscribe
的時候,我們就向 stateStore
註冊了一個回呼函式 (callback function),也就是 (state) => value = state
。每當 stateStore
當中的變數發生改變,就會執行這個回呼函式。同時,當我們呼叫 subscribe
的時候,也得到了一個函式,也就是 unsubscribe
。當我們執行這個 unsubscribe
函式,那麼先前利用 subscribe
向 stateStore
註冊的回呼函式 (callback function) 就會作廢。
第十四行:onDestroy(unsubscribe);
當 Svelte 元件從 DOM 卸載時,執行 unsubscribe
函式。這麼一來,我們的 Store 就不會一直想要試著連動已經卸載的 Svelte 元件當中的變數了。
圖二、記得做好善後工作
這麼一來就完美的將 Javascript 的變數跟 Svelte 元件中的變數連動在一起了。不過這樣子寫似乎有些麻煩。首先要先使用 subscribe
做出連動,接著還不能忘記元件卸載時要執行 unsubscribe
避免浪費記憶體空間資源。當我們需要連動的變數越來越多,這樣的程式碼寫起來似乎會越來越頭痛。
因此 Svelte 很貼心的提供了一個簡單的方法來做到這個效果,也就是自動訂閱 (auto-subscription)。來看看怎麼做吧:
/src/App.svelte
<script>
import { onMount } from 'svelte';
import { stateStore } from './store.js';
$: value = $stateStore;
onMount(() => {
const interval = setInterval(() => stateStore.update((state) => state + 1), 1000);
return () => clearInterval(interval)
})
</script>
<h1>Now is {value}!</h1>
第二行:import { onMount } from 'svelte';
使用自動訂閱的方式,會直接幫我們連接/解除連接,所以不需要再引入 onDestroy
去處理卸載時的例行公事了。
第五行:$: value = $stateStore;
前面提過,stateStore
其實是 Svelte 提供的 Store 物件,當中儲存著一個變數。這個變數可以透過 set
、update
等方法來做改變,同時也可以透過 subscribe
這個方法連動到 Svelte 元件的變數,藉此來取得 Store 物件當中的變數的值。繞了很大一圈不是嗎?還因此需要準備 unsubscribe
這個方法來避免記憶體資源浪費。所以 Svelte 更進一步提供了自動訂閱的功能來簡化這個流程。自動訂閱,另一種說法就是可以讓我們直接從 Store 物件當中取得變數的值。怎麼做呢?stateStore
是 Store 物件,$stateStore
就是 Store 物件當中的變數的值。並且用 $: value
來做出互動宣告(詳見第 08 天:Svelte 中的 Javascript:宣告)的效果。只要 Store 物件當中的變數一改變,value
就會因為重新賦值而一起更新。
既然 $stateStore
可以直接取得 stateStore
這個 Store 物件當中的變數的值,那麼這一段程式碼似乎還有簡化的空間:
/src/App.svelte
<script>
import { onMount } from 'svelte';
import { stateStore } from './store.js';
// $: value = $stateStore;
onMount(() => {
const interval = setInterval(() => stateStore.update((state) => state + 1), 1000);
return () => clearInterval(interval)
})
</script>
<h1>Now is {$stateStore}!</h1>
第五行:// $: value = $stateStore;
既然我們可以透過 $stateStore
直接取得 Store 物件當中變數的值,那是不是可以直接拿來用,也不需要這一行了。
第十三行:<h1>Now is {$stateStore}!</h1>
直接在 HTML 元素當中展開 Javascript 的領域,並且使用 $stateStore
直接取得 Store 物件當中變數的值。
圖三、利用自動訂閱,簡化再簡化
那麼今天關於 Store 的討論就到這邊了,謝謝大家。