凱瑟琳彎過身來悄聲對我說:「我的專案狀態 (state) 跟文件物件模型 (DOM) 總是沒辦法同步。」
「他們沒辦法同步嗎?」
她的眼神先是看向莫爾朵,接著轉向湯姆:「一點辦法也沒有。」~節錄自《The Great Svelte:第二章》
系列文介紹到這邊,我們已經差不多認識了 Svelte 專案的檔案架構,Svelte 元件當中 HTML 跟 CSS 的用法,今天要開始來介紹 Svelte 當中的 Javascript,是如何被重新詮釋的既強大又平易近人。
總的來說,互動式使用者介面 (reactive user interface) 設計的概念,是以 Javascript 記錄下介面的狀態,當狀態改變時,介面呈現的內容跟著改變,藉此達到互動的效果。
而我們已經知道如何在 Svelte 元件當中,用大括弧 {}
將 Javascript 的狀態寫入 HTML 元素裡面了 (見第 05 天)。剩下的就是如何改變 Javascript 的狀態,以及如何讓 HTML 隨著 Javascript 狀態的改變而進行更新。
若是從純純的 Javascript (Vanilla Javascript) 出發,改變 Javascript 的狀態跟更新 HTML 的元素是兩件事,所以會看到兩行程式碼:
let someState = updatedState;
document.querySelector('someSelector').innerText = someState;
在 Svelte 當中,則是透過編譯器 (compiler) 來自動處理這些工作,專案開發者只要簡簡單單的寫下一行程式碼就行。哪一行呢?就是這一行:
let someState = updatedState;
也就是說,只要在 Javascript 透過賦值 (assignment) 這個動作,Svelte 的編譯器就會自動編譯出可以隨著賦值而進行更新的 HTML 元素了。這也太神奇了吧,趕快讓我們來試試吧。我們來修改 Counter.svelte
這個元件,讓元件當中呈現的數字隨著時間而更新看看:
/src/lib/Counter.svelte
<script>
let count = 0;
const countArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
countArray.forEach(x => {
setTimeout(() => count = x, x * 1000);
});
</script>
<section>
<h1>Create Color Palette for Me!</h1>
<div class='counter'>
<button>
<svg aria-hidden="true" viewBox="0 0 1 1">
<path d="M0,0.5 L1,0.5" />
</svg>
</button>
<div class="counter-viewer">
<p>{count}</p>
</div>
<button>
<svg aria-hidden="true" viewBox="0 0 1 1">
<path d="M0,0.5 L1,0.5 M0.5,0 L0.5,1" />
</svg>
</button>
</div>
</section>
第一行:<script>
建立一個 Javascript 運作的段落。
第二行:let count = 0;
宣告一個變數 count
。
第三行:const countArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
宣告一個陣列變數 countArray
,當中放進我們想讓 count
隨著時間而更新的值。
第五行:setTimeout(() => count = x, x * 1000);
設定好我們的計時器,讓 count
隨著時間而更新成不同的值。
第十九行:<p>{count}</p>
接著在 HTML 元素當中使用這個變數 count
。
然後就沒有然後了。來看看我們的成果吧:
圖一、1、2、3、4、5、6、7、8、9、10!
什麼,這麼神奇,居然只要這樣就行!沒錯,所有的辛苦工作編譯器都幫我們完成了。
由於編譯器可以偵測 Javascript 的變數被使用在哪些地方,同時 Svelte 也重新定義了 let
以及 =
,所以開發者們只要利用賦值 (assignment, =
) 來更新 Javascript 的變數,Svelte 的編譯器就會自動找出需要跟著更新的地方,並且打造出能夠隨著 Javascript 變數而更新的使用者介面。
最後要提醒一下,因為 Svelte 是藉由賦值 (assign, =
) 這個動作來偵測變數的改變,在這以外,如果變數有任何改變,Svelte 可是沒辦法處理的。舉例來說,Javascript 陣列的函式,有些是會原地 (in-place) 改變陣列的資料內容的。當我們在 Svelte 當中用這種函式來改變變數,並不會觸發使用者介面的更新,因此就沒辦法即時將 Javascript 的狀態回饋到使用者介面上了。
那應該怎麼做呢?舉例來說:
let numArray = [0, 9, 2, 2];
numArray.sort()
// 這時 numArray 就會變成 [0, 2, 2, 9],但 Svelte 會渾然不覺
因為我們並不是透過賦值來更動 numArray
這個變數,所以 Svelte 會渾然不覺這個變數已經不同了。那怎麼辦呢?這個時候只要加一個動作來觸發 Svelte 偵測變數改變就行了:
let numArray = [0, 9, 2, 2];
numArray.sort()
// 這時 numArray 就會變成 [0, 2, 2, 9],但 Svelte 會渾然不覺
numArray = numArray
// 沒錯就是這麼簡單
沒錯,就是把變數重新賦值一次就行。所以原則很簡單,只要看到 =
,Svelte 就能夠幫我們追蹤變數!那麼今天的內容就到這邊了,謝謝各位讀者。