這是〈Svelte 從小白到入門〉系列的第二集,各集連結如下:


在第一集我們談到了元件的概念,也提到元件是彼此隔離的,並且 Svelte 也提供了讓元件與元件間互相傳遞值的機制,這些被丟來丟去的值在 Svelte 的世界稱為 properties,簡稱 props。

Props

在 Nested 元件裡,我們用 exportanswer 對外暴露:

在 App 元件中,第一次調用 Nested 元件時我們對 answer 賦值 42,因此網頁上呈現的是「The answer is 42」。

而在第二次調用 Nested 時,我們不賦值,那 answer 的值就會是原本的 a mystery,因此網頁上呈現的是「The answer is a mystery」。

「若在 Nested 內,不對 answer 賦值,並且調用 Nested 也不賦值的話,網頁會顯示什麼呢?」此時的 answer 會是 undefined,因此網頁也會顯示成「The answer is undefined」。

看完上面的範例,好像會以為一個元件內一定要有 export 個什麼變數,該元件才能有個 props,實際上並非如此,其實 App 元件可以傳遞任意的值(props)進 Nested 元件,這些在 Nested 內並未預先定義的 props 會全部儲存在一個特殊的物件 $$props 內。

拿上一個範例改造一下:

在 Nested 元件內我們沒有定義任何變數,而是直接調用傳進來的 $$props.answer 做顯示。

看起來好像很神奇,但 Svelte 文件說這會使程式難以優化,它應該是指 compile 時難以針對這種非預期的 props 做效能優化,因此建議盡可能還是在元件內明確宣告要 export 的變數。

Props 展開

如果打算要傳入的值都包裝在一個物件內,並且物件內的名稱和 props 的名稱故意取的一樣,那 Svelte 可以讓我們少打很多字。

下面的範例,所有要傳進 Info 的值都包在 pkg 內,並且 pkg 內的物件命名和 Info 的 props 命名一致,如此我們可以用 {...pkg} 的形式把值傳入 Info 元件,Svelte 會幫我們把 pkg 展開丟到對應的 props:

上面我們在 App 內調用了兩次 Info 元件,第二次我們用上了 props 展開的奇計淫巧,讓我們少打了不少字。

Svelte 元件內的邏輯

<script> 區塊內的邏輯處理,沿用既有的 JS 語法標準即可,但若是要在 Svelte 元件的 HTML 內做邏輯處理,那又得用上 Svelte 特有的語法…。

綜觀而言,這些特有語法都在破壞 JS 的一致性,我們只能把他們視為一種 DSL,跟著遊戲規則玩下去。

扯遠了,回到主題。

if / else

想要在 Svelte 元件內的 HTML 插入 if 區塊,用 {#if} 開頭,中間是 {:else},最後以 {/if} 結尾將上下文包起來。

從這個例子可以看到 Svelte HTML 邏輯的固定模式:

迴圈

延續上面的概念,要建立迴圈,也是用同樣的結構,當然,Svelte 的特有語法 {#each cats as cat, i} 還是要記一下的,其中的第二個參數 i 代表從 0 開始的陣列索引號:

迴圈加 key

迴圈也具有 reactivity 特性,一旦來源的 JS 陣列物件變動,相對應的 HTML 迴圈結構也會變動,以 things 陣列砍掉第一個成員為例,此時的行為是:

  1. JS 陣列物件變動,去掉第一個成員
  2. 引發 HTML 迴圈結構變動,HTML 迴圈也會減掉一個成員,但要注意的是,HTML 迴圈的成員加減是作用在最末,也就是最後一個成員被去掉
  3. 此時 JS 陣列與 HTML 迴圈數量一致,reactivity 機制再把雙方的數值同步

上面的示例展示在下方,動手玩玩看會發現文字與圖案有不同步的現象:

按鈕後,因為 things = things.slice(1),文字的 apple 不見了,但在圖案那邊,由於上述第二點的關係,卻是最後一個 🥕 不見,導致兩邊不同步。

要改變這樣的行為,可以為迴圈內的元素加上 key,有了神奇的 key,上述第二點的行為就會變為:

  • Svelte 會去比較變更前後 key 的變化,並反映到相對的 HTML 陣列上

在下面的示例我們為 things 內的水果們都加上了 id 作為他們的唯一識別,並在 HTML 迴圈內以 {#each things as thing (thing.id)} 的形式將 thing.id 定義為各個水果的 key,一旦 things 發生變動,Svelte 會去比較 thing.id 的變化,並反映在 HTML 陣列元素上:

至此,Svelte 奇怪的語法又增加了一點。

本節的例子完全是為了講解迴圈的 key 而設的,回過頭說文字圖案不同步的問題,其實也可以直接幫 Thing 元件內的 emoji 加上 reactivity 的特性就好,如 $: emoji = emoji[name];

Await

也可以在 Svelte 元件的 HTML 內呼叫 async 函式,用法頗直觀,{#await}{:then}{:catch}{/await} 四部曲:

另外可以注意到,在 handleClick() 裡面,我們並非是直接呼叫 getRandomNumber(),而是用了再次對 promise 賦值的方式,曲線執行 getRandomNumber(),解決了 await 函式無法被一般函式呼叫的問題。(或者說得一路 async / await 連鎖下去的問題)

小結

整理本集重點:

  • 元件間被丟來丟去的值通稱為 props
  • 元件內用 export 明確定義要對外暴露的 props
  • 所有被丟入元件的 props 都會存入 $$props 這個物件內
  • Svelte 元件內 HTML 可置入邏輯語法,以 if / else 為例,結構是 {#if}{:else if}{:else}{/if}
  • 迴圈可以加上 id 與 key

(待續)