這是〈Svelte 從小白到入門〉系列的第二集,各集連結如下:
- 〈Svelte 從小白到入門(一)元件、事件、Reactivity〉
- 〈Svelte 從小白到入門(二)Props、邏輯、迴圈、Await〉
- 〈Svelte 從小白到入門(三)事件〉
- 〈Svelte 從小白到入門(四)數據綁定〉
- 〈Svelte 從小白到入門(五)生命週期〉
- 〈Svelte 從小白到入門(六)Store〉
- 〈Svelte 從小白到入門(七)動態效果〉
- 〈Svelte 從小白到入門(八)進出場效果〉
- 〈Svelte 從小白到入門(九)動畫效果〉
- 〈Svelte 從小白到入門(十)Action〉
- 〈Svelte 從小白到入門(十一)CSS Class〉
- 〈Svelte 從小白到入門(十二)Slot〉
- 〈Svelte 從小白到入門(十三)Context〉
- 〈Svelte 從小白到入門(十四)Svelte 的特殊元素〉
- 〈Svelte 從小白到入門(十五)Module Context〉
- 〈Svelte 從小白到入門(十六)Debugging〉
- 〈Svelte 從小白到入門(十七)結束與展望〉
在第一集我們談到了元件的概念,也提到元件是彼此隔離的,並且 Svelte 也提供了讓元件與元件間互相傳遞值的機制,這些被丟來丟去的值在 Svelte 的世界稱為 properties,簡稱 props。
Props
在 Nested 元件裡,我們用 export
令 answer
對外暴露:
在 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
陣列砍掉第一個成員為例,此時的行為是:
- JS 陣列物件變動,去掉第一個成員
- 引發 HTML 迴圈結構變動,HTML 迴圈也會減掉一個成員,但要注意的是,HTML 迴圈的成員加減是作用在最末,也就是最後一個成員被去掉
- 此時 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
(待續)