each
邏輯區塊(二)each
邏輯段落使用解構賦值each
段落 經過昨天的努力,我們非常有效率的把色票產生器的版面雛型做出來了。有準備用來改變色票數量的計數器,也有三張不同顏色的色票,包含一個填滿色票顏色的色塊,以及代表色票的 hex 代碼 ,最重要的是,我們還學會了如何用 each
這個邏輯段落毫不費力的來產生重複性的 HTML 模板。
今天就讓我們繼續熟悉 each
邏輯段落的使用,並且讓色票計數器與色票連動起來吧。
each
邏輯段落使用解構賦值 在 Svelte 當中使用 each
邏輯段落的手法就跟 Javascript 的陣列 (array) 方法十分類似。都是先以一個陣列作為主體,再依序迭代陣列當中的各個項目,藉此減少繁瑣的重複工作。既然 Svelte 提供的 each
看起來這麼的「Javascript」,我們是不是也可以更「Javascript」的來使用這個 each
呢?比方說,來做個 Javascript 的解構賦值:
/src/lib/Palettes.svelte
<script>
import unlock from "../assets/unlock.svg";
let palettes = [
{ hex: "ff4000", locked: false, id: "init_01" },
{ hex: "32e6e3", locked: false, id: "init_02" },
{ hex: "009fe9", locked: false, id: "init_03" },
];
</script>
<div class="palettes">
{#each palettes as { hex }}
<div class="card">
<div class="palette" style="background: #{hex}" />
<div class="hex-code">
<p>
{hex}
</p>
</div>
<div class="lock-icon">
<img src={unlock} alt="color-unlocked" />
</div>
</div>
{/each}
</div>
第三行:let palettes = [
重新定義我們的變數 palettes
,一樣是一個代表陣列的變數,只是陣列的項目改成一個一個物件 (object)。
第四行:{ hex: "ff4000", locked: false, id: "init_01" },
將每一個色票用物件來表示,除了代表顏色的 hex
這個性質之外,另外再加上 locked
跟 id
。第五行跟第六行也是相同的做法,就不贅述。
第十一行:{#each palettes as { hex }}
利用 {#each}
開啟我們的 each
邏輯段落。基本的語法就跟昨天介紹過的相同,差別只差在我們今天特別用解構賦值的方式直接把物件當中的 hex
的值取出來。
第十三行:<div class="palette" style="background: #{hex}" />
首先用大括弧 {}
展開 Javascript 的領域,接著直接使用解構賦值取出的 hex
來使用。
第十六行:{hex}
首先用大括弧 {}
展開 Javascript 的領域,接著直接使用解構賦值取出的 hex
來使用。
如何,解構賦值的結果是不是就跟預想中相同呢。
圖一、看不出差別的解構賦值
each
段落 接下來要講一個現階段看不出差別,但以後會用到的功能,也就是具有鍵值的 each
段落 (keyed each
block)。所謂具有鍵值的 each
段落,語法是這樣寫的:
<!-- 如果不需要 index -->
{#each array as item (key)}...{/each}
<!-- 如果需要 index -->
{#each array as item, index (key)}...{/each}
我們先前用的 each
段落,都是沒有加上鍵值的 each
段落,看起來似乎也運作如常。那有沒有加上鍵值的影響是什麼呢?
如果有提供鍵值的話,當 Javascript 的狀態改變進而影響 HTML 元素的呈現時,Svelte 會根據這個鍵值來區別 HTML 的元素,並判斷應該要在哪邊加上或是刪減哪些 HTML 元素。因此加上鍵值的 each
段落,是想要做出流暢轉場所不可或缺的要素。
需要注意的是作為鍵值的變數不能重複,必須要讓 Svelte 能夠藉由這個鍵值來區分每一個由 each
所產生出來的段落。所以看看我們替每一個色票所準備的 id
:"init_01"
、"init_02"
、"init_03"
,是不是都是獨一無二的呢!
今天雖然我們還不會使用到 Svelte 提供的華麗轉場效果,但就讓我們先補上鍵值以備不時之需:
/src/lib/Palettes.svelte
<script>
import unlock from "../assets/unlock.svg";
let palettes = [
{ hex: "ff4000", locked: false, id: "init_01" },
{ hex: "32e6e3", locked: false, id: "init_02" },
{ hex: "009fe9", locked: false, id: "init_03" },
];
</script>
<div class="palettes">
{#each palettes as { hex, id } (id)}
<div class="card">
<div class="palette" style="background: #{hex}" />
<div class="hex-code">
<p>
{hex}
</p>
</div>
<div class="lock-icon">
<img src={unlock} alt="color-unlocked" />
</div>
</div>
{/each}
</div>
{#each palettes as { hex, id } (id)}
each
段落加上鍵值,唯一需要修改的一行程式碼就是開啟 each
邏輯段落的這一行。首先用解構賦值把物件當中的 hex
跟 id
拿出來。接著用一個小括弧 ()
把 id
放進去作為 each
段落的鍵值。這樣一來就搞定了。
圖二、還是看不出任何差別的鍵值 each
段落
今天最後一個部分就是把色票的變動跟計數器的變動串在一起。為了要能夠讓計數器 (Counter.svelte
) 需要用的變數 count
跟色票 (Palettes.svelte
) 需要用的變數 palettes
能互相溝通,就讓我們把 palettes
也轉而放到主要的元件 (App.svelte
) 當中,並且透過 Property 的方式傳遞給色票元件吧。首先重新修改我們的 App.svelte
跟 Palettes.svelte
:
/src/App.svelte
<!-- 在 Javascript 當中 import Counter -->
<script>
import Counter from "./lib/Counter.svelte";
import Modal from "./lib/Modal.svelte";
import Palettes from "./lib/Palettes.svelte";
let showModal = false;
let palettes = [
{ hex: "ff4000", locked: false, id: "init_01" },
{ hex: "32e6e3", locked: false, id: "init_02" },
{ hex: "009fe9", locked: false, id: "init_03" },
];
$: count = palettes.length;
let someState = "TheGreatSvelte";
const sparkle = (text) => {
const sparkles = ["★", "☆", "✧", "✪"];
const randomSparkles = () =>
sparkles[Math.floor(Math.random() * sparkles.length)];
const sparkledText = text
.split("")
.reduce((a, c) => a + randomSparkles() + c, "");
return sparkledText;
};
const href = "https://ithelp.ithome.com.tw/users/20120178/ironman/7031";
const handleClick = (e) => {
console.log(e);
const tobeCount = count + e.detail;
if (!(tobeCount > 87)) count = tobeCount;
else showModal = true;
};
</script>
<main>
<!-- 在 HTML 當中直接嵌入 Counter -->
<Counter {count} on:changeCount={handleClick} />
<Palettes {palettes} />
<p class="comment">
Check out <a {href}>Svelte Tutorial</a>, the awesome article powered by {sparkle(
someState
)}!
</p>
</main>
<Modal {showModal} on:closeModal={() => (showModal = false)} />
第九行:let palettes = [
將原本放在 Palettes.svelte
的變數 palettes
寫進 App.svelte
。
第十五行:$: count = palettes.length;
同時利用 $:
將 count
做成衍生自 palettes.length
的變數,只要 palettes.length
改變,count
就會自動跟著更新。
第四十二行:<Palettes {palettes} />
因為我們已經把 palettes
拿到 App.svelte
來了,為了讓 Palettes.svelte
也知道 palettes
的內容,這邊用元件 Property 的形式,把 palettes
的資料內容傳給 Palettes.svelte
。
接著在 Palettes.svelte
補上這麼一行來接收 palettes
:
/src/lib/Palettes.svelte
<script>
import unlock from "../assets/unlock.svg";
export let palettes;
</script>
<div class="palettes">
{#each palettes as { hex, id } (id)}
<div class="card">
<div class="palette" style="background: #{hex}" />
<div class="hex-code">
<p>
{hex}
</p>
</div>
<div class="lock-icon">
<img src={unlock} alt="color-unlocked" />
</div>
</div>
{/each}
</div>
export let palettes;
export
宣告一個做為元件 Property 的變數 palettes
,用來接收外部元件傳入的資料。
圖三、還是不會跟著計數器改變的色票
雖然我們的 count
一開始是由 palettes.length
來決定,但還記的嗎?一開始的時候我們也讓計數器的加減擁有改變 count
的能力,所以整個專案運作起來才變得怪怪的。現在要做修正,只要讓計數器的加減直接改變 palettes
當中色票的數目就可以。計數器加一,就要在 palettes
當中放入一個新的色票。計數器減一,就要從 palettes
把一個色票移除。而 count
永遠靠著 $: count = palettes.length;
這一行程式碼來做即時更新。
/src/App.svelte
<!-- 在 Javascript 當中 import Counter -->
<script>
import Counter from "./lib/Counter.svelte";
import Modal from "./lib/Modal.svelte";
import Palettes from "./lib/Palettes.svelte";
let showModal = false;
let palettes = [
{ hex: "ff4000", locked: false, id: "init_01" },
{ hex: "32e6e3", locked: false, id: "init_02" },
{ hex: "009fe9", locked: false, id: "init_03" },
];
$: count = palettes.length;
const generateTone = () =>
("0" + Math.round(255 * Math.random()).toString(16)).slice(-2);
const generateHex = () =>
[generateTone(), generateTone(), generateTone()].reduce(
(a, c) => a + c,
""
);
const generatePalette = (() => {
let uuid = 0;
return () => ({
hex: generateHex(),
locked: false,
id: `${uuid++}`,
});
})();
let someState = "TheGreatSvelte";
const sparkle = (text) => {
const sparkles = ["★", "☆", "✧", "✪"];
const randomSparkles = () =>
sparkles[Math.floor(Math.random() * sparkles.length)];
const sparkledText = text
.split("")
.reduce((a, c) => a + randomSparkles() + c, "");
return sparkledText;
};
const href = "https://ithelp.ithome.com.tw/users/20120178/ironman/7031";
const handleClick = (e) => {
console.log(e);
const tobeCount = count + e.detail;
if (0 < tobeCount && tobeCount < 7) {
switch (e.detail) {
case 1:
palettes = [...palettes, generatePalette()];
break;
case -1:
palettes = palettes.slice(0, -1);
break;
}
} else showModal = true;
};
</script>
我們單單就 App.svelte
當中 Javascript 的部分做說明。
第九行:let palettes = [
並沒有做改變,就是宣告一個 palettes
。
第十五行:$: count = palettes.length;
並沒有做改變,就是讓 count
的數量隨著 palettes.length
而即時更新。
第十七行:const generateTone = () =>
宣告一個函式,用來產生一個十六進位的數字。
第十九行:const generateHex = () =>
宣告一個函式,用來產生一個 hex 色碼。
第二十四行:const generatePalette = (() => {
宣告一個函式,用來產生一個可以放進 palettes
的物件。
第四十六行:const handleClick = (e) => {
修改我們的 handleClick
這個函式。
第四十九行:if (0 < tobeCount && tobeCount < 7) {
把 tobeCount
限縮在 1
到 6
之間。
第五十二行:palettes = [...palettes, generatePalette()];
如果是加,就產生一個 palette
物件並放入 palettes
當中。
第五十五行:palettes = palettes.slice(0, -1);
如果是減,就把最末端的一個 palette
物件給移除。
圖四、我們的色票產生器終於動起來囉
這就是今天關於 each 運作邏輯的介紹以及色票產生器的邏輯設計了,相關的程式碼可以在 Github 資源庫找到。謝謝各位讀者支持。