iT邦幫忙

2023 iThome 鐵人賽

DAY 17
0
Modern Web

了不起的 Svelte系列 第 17

第 17 天:Svelte 中的邏輯運作:`each` 邏輯區塊(二)

  • 分享至 

  • xImage
  •  

第 17 天:Svelte 中的邏輯運作:each 邏輯區塊(二)

第 17 天要講的事

  1. each 邏輯段落使用解構賦值
  2. 具有鍵值的 each 段落
  3. 重新整理色票產生器的邏輯設計

  經過昨天的努力,我們非常有效率的把色票產生器的版面雛型做出來了。有準備用來改變色票數量的計數器,也有三張不同顏色的色票,包含一個填滿色票顏色的色塊,以及代表色票的 hex 代碼 ,最重要的是,我們還學會了如何用 each 這個邏輯段落毫不費力的來產生重複性的 HTML 模板。
  今天就讓我們繼續熟悉 each 邏輯段落的使用,並且讓色票計數器與色票連動起來吧。

1. 在 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 這個性質之外,另外再加上 lockedid。第五行跟第六行也是相同的做法,就不贅述。

  • 第十一行:{#each palettes as { hex }}
      利用 {#each} 開啟我們的 each 邏輯段落。基本的語法就跟昨天介紹過的相同,差別只差在我們今天特別用解構賦值的方式直接把物件當中的 hex 的值取出來。

  • 第十三行:<div class="palette" style="background: #{hex}" />
      首先用大括弧 {} 展開 Javascript 的領域,接著直接使用解構賦值取出的 hex 來使用。

  • 第十六行:{hex}
      首先用大括弧 {} 展開 Javascript 的領域,接著直接使用解構賦值取出的 hex 來使用。

  如何,解構賦值的結果是不是就跟預想中相同呢。

https://d1dwq032kyr03c.cloudfront.net/upload/images/20231001/20120178GigOU1xZN2.png
圖一、看不出差別的解構賦值

2. 具有鍵值的 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 邏輯段落的這一行。首先用解構賦值把物件當中的 hexid 拿出來。接著用一個小括弧 ()id 放進去作為 each 段落的鍵值。這樣一來就搞定了。

https://d1dwq032kyr03c.cloudfront.net/upload/images/20231001/20120178GigOU1xZN2.png
圖二、還是看不出任何差別的鍵值 each 段落

3. 重新整理色票產生器的邏輯設計

  今天最後一個部分就是把色票的變動跟計數器的變動串在一起。為了要能夠讓計數器 (Counter.svelte) 需要用的變數 count 跟色票 (Palettes.svelte) 需要用的變數 palettes 能互相溝通,就讓我們把 palettes 也轉而放到主要的元件 (App.svelte) 當中,並且透過 Property 的方式傳遞給色票元件吧。首先重新修改我們的 App.sveltePalettes.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,用來接收外部元件傳入的資料。

https://i.ibb.co/PcJdnB1/17.gif
圖三、還是不會跟著計數器改變的色票

3. 重新整理色票產生器的邏輯設計

  雖然我們的 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 限縮在 16 之間。

  • 第五十二行:palettes = [...palettes, generatePalette()];
      如果是加,就產生一個 palette 物件並放入 palettes 當中。

  • 第五十五行:palettes = palettes.slice(0, -1);
      如果是減,就把最末端的一個 palette 物件給移除。

https://i.ibb.co/DDQKbqN/17.gif
圖四、我們的色票產生器終於動起來囉

  這就是今天關於 each 運作邏輯的介紹以及色票產生器的邏輯設計了,相關的程式碼可以在 Github 資源庫找到。謝謝各位讀者支持。


上一篇
第 16 天:Svelte 中的邏輯運作:`each` 邏輯區塊(一)
下一篇
第 18 天:Svelte 中的邏輯運作:`each` 邏輯區塊(三)
系列文
了不起的 Svelte30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言