iT邦幫忙

2023 iThome 鐵人賽

DAY 6
0
Modern Web

了不起的 Svelte系列 第 6

第 06 天:Svelte 中的 CSS

  • 分享至 

  • xImage
  •  

第 06 天:Svelte 中的 CSS

她就這麼大辣辣地走了進來,以一種檢視財產的姿態,將房間裡的 CSS 選擇器都看了個遍,讓我不禁思索她是否會干擾到他們。當我這麼問她時,她笑了出來,強調似的把我的問題重複了一遍,然後告訴我她不會。

~節錄自《The Great Svelte:第二章》

第 06 天要講的事

  1. 在 Svelte 元件中使用 CSS
  2. 使用內嵌元件 (nested component)

  昨天的文章內容,我們說明了 Svelte 元件是如何擴展 HTML 的功能,藉由在 HTML 當中使用大括弧 {} 展開 Javascript 領域,使得 HTML 呈現的內容可以隨著 Javascript 的變化做出調整,進而讓我們能夠更輕鬆地寫出互動式的前端專案。
  今天就來講講 Svelte 又對 CSS 做了哪些擴充。

在 Svelte 元件中使用 CSS

  我們在第四天的文章中說明過,要在 Svelte 元件裡使用 CSS 來渲染元件的格式,只要用 <style></style> 展開屬於 CSS 的領域就可以了。在 CSS 的領域之內,程式碼一切的寫法都跟一般的 CSS 並無二致。
  讓我們再多寫一些 CSS 看看。首先,為了要讓 CSS 有更多的發揮空間,讓我們加入更多的 HTML 元素吧。以下程式碼先展示的是 App.svelte 當中屬於 HTML 的部分:

/src/App.svelte
<main>
  <!-- 多加入 section 這個段落 -->
  <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>1</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>

  <p class='comment'>Check out <a {href}>Svelte Tutorial</a>, the awesome article powered by {sparkle(someState)}!</p>
</main>
  • 第三行:<section>
      加入一個 <section>

  • 第四行:<h1>Create Color Palette for Me!</h1>
      加入一個 <h1></h1>

  • 第六行:<div class='counter'>
      加入一個 <div class='counter'>

  • 第七行:<button>
      加入一個 <button>,並緊接著在當中放入 <svg>

  • 第十一行:<div class="counter-viewer">
      加入一個 <div class="counter-viewer">,並在當中放入一個 <p>

  • 第十四行:<button>
      再放入一個 <button>,同樣在其中放入 <svg>

  全部 HTML 元素都擺上去之後,npm run dev 來看一下我們現在的專案,是不是多了不少內容,但因為沒有 CSS 的修飾,看起來稍嫌尷尬呢?

https://ithelp.ithome.com.tw/upload/images/20230921/201201783TvWSpJL2z.png
圖一、尷尬又不失禮貌的 HTML

  那麼就讓我們快快補上一些 CSS 吧!以下程式碼顯示的同樣是 App.svelte 當中的 CSS 段落:

/src/App.svelte
<style>
  main {
    min-height: 100vh;
    max-width: 500px;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
  }

  p.comment {
    color: #888;
    margin: 1em 0.5em;
  }

  p.comment a {
    text-decoration: none;
  }

  /* 以下為新增加的 CSS,用來修飾新增加的 section */
  section {
    display: flex;
    flex-direction: column;
    align-items: center;
  }

  h1 {
    margin: 0.5em 0;
  }

  .counter {
    display: flex;
    justify-content: center;
  }

  .counter button {
    width: 4em;
    height: 4em;
    border-radius: var(--border-radius);
    background: var(--color-bg-3);
    outline: none;
    border: none;
    cursor: pointer;
  }

  .counter button:hover {
    background: #fff;
  }

  .counter .counter-viewer {
    width: 4em;
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .counter .counter-viewer p {
    font-size: 2em;
    font-weight: 600;
  }

  svg {
    width: 25%;
    height: 25%;
  }

  path {
    vector-effect: non-scaling-stroke;
    stroke-width: 2px;
    stroke: #444;
  }
</style>

  這個 CSS 段落有點長,這邊就不一一解釋每一段程式碼的功能了。不過總而言之,這些都是標準的 CSS 寫法,也就是說,只要在 Svelte 元件當中以 <style></style> 開啟 CSS 的段落,就可以完全使用熟悉的 CSS 語言來渲染我們的 HTML 元素囉!

https://ithelp.ithome.com.tw/upload/images/20230921/20120178h1UhKVQGmd.png
圖二、CSS 修飾過後的 HTML

使用內嵌元件 (nested component)

  這樣看起來,Svelte 似乎沒有對 CSS 做什麼擴展的感覺?其實是有的,只是以目前的專案架構還看不太出來。為了說明 Svelte 對 CSS 做了哪些擴展,讓我們介紹一個新觀念,並稍微修改目前的專案架構吧!
  這個新觀念就是內嵌元件 (nested component)。當我們的檔案越寫越複雜,要將所有的程式碼,從 Javascript、HTML、到 CSS,全部放進同一個 Svelte 檔案,似乎是一件相當可怕的事情。看看我們現在的 App.svelte。光是要做出目前的樣貌,程式碼數一數看起來也都超過 100 行了,更別說這才只是剛開始而已。所有的程式碼都塞在同一個檔案,不僅撰寫的時候容易迷失,之後要維護、偵錯的時候也相當困難。
  這時候我們就需要使用內嵌元件了。也就是將 Svelte 元件依照功能拆分成更多小的 Svelte 元件,藉由引入 (import) 這些小的 Svelte 元件來組合而成一個完整的 Svelte 元件。
  我們只要將欲拆分出來的程式碼另外寫成一個副檔名為 svelte 的檔案,並藉由 import 將該檔案內容內嵌在我們想要使用的地方即可。以今天實作的內容來說,我想要拆分出來的內容就是新增加的 <section>。一起來寫寫看吧:

src/lib/Counter.svelte`
<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>1</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>

<style>
  /* 以下為新增加的 CSS,用來修飾新增加的 section */
  section {
    display: flex;
    flex-direction: column;
    align-items: center;
  }

  h1 {
    margin: 0.5em 0;
  }

  .counter {
    display: flex;
    justify-content: center;
  }

  .counter button {
    width: 4em;
    height: 4em;
    border-radius: var(--border-radius);
    background: var(--color-bg-3);
    outline: none;
    border: none;
    cursor: pointer;
  }

  .counter button:hover {
    background: #fff;
  }

  .counter .counter-viewer {
    width: 4em;
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .counter .counter-viewer p {
    font-size: 2em;
    font-weight: 600;
  }

  svg {
    width: 25%;
    height: 25%;
  }

  path {
    vector-effect: non-scaling-stroke;
    stroke-width: 2px;
    stroke: #444;
  }
</style>

  我在上面將所有落落長的程式碼都寫了出來,目的只是想說明我們確實只擷取 <section> 以及子元素的相關程式碼,並將這些程式碼存成另一個檔案,也就是 Counter.svelte。檔案放置的位置是在 /src/lib 這個資料夾當中。這時候我們的專案架構看起來會像這樣了:

C:.
│   .gitignore
│   index.html
│   jsconfig.json
│   package-lock.json
│   package.json
│   README.md
│   svelte.config.js
│   vite.config.js
│
├───.vscode
├───node_modules
├───public
│       vite.svg
│
└───src
    │   app.css
    │   App.svelte
    │   main.js
    │   vite-env.d.ts
    │
    ├───assets
    │       svelte.svg
    │
    └───lib
            Counter.svelte

  接著就是在 App.svelte 當中將 Counter.svelte 這個元件嵌入進來:

/src/App.svelte
<!-- 在 Javascript 當中 import Counter -->
<script>
  import Counter from './lib/Counter.svelte';

  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';
</script>

<main>
  <!-- 在 HTML 當中直接嵌入 Counter -->
  <Counter />

  <p class='comment'>Check out <a {href}>Svelte Tutorial</a>, the awesome article powered by {sparkle(someState)}!</p>
</main>

  這邊為了方便起見,我只寫出 Javascript 以及 HTML 的部分,CSS 就略過不寫出來了。

  • 第三行:import Counter from './lib/Counter.svelte';
      先在 Javascript 當中利用 import 引入我們需要的 Svelte 元件。

  • 第十八行:<Counter />
      就像在寫一個 HTML 元素一樣,直接將我們引入的 Svelte 元件放在需要的地方。

  如此就完成引入 Svelte 元件的動作了。是不是很簡單呢!
  那麼回過頭來問個問題:不同 Svelte 元件的 CSS 會互相干擾嗎?答案是不會。一個 Svelte 元件當中的 CSS 設定,只會渲染到本身的 Svelte 元件,不管不同 Svelte 如何搭配組合,都不會互相影響。這也是 Svelte 賦予 CSS 新的一個特色。這個特色說出來很直覺,但卻非常重要。唯有 CSS 不會互相干擾,才能輕鬆做出真正獨立的 Svelte 元件。那麼讓我們來試試看所謂不會互相干擾是什麼意思呢,我們試著把 Counter.svelte 當中的 .counter .counter-viewer p 這個 CSS 選擇器改成 p 就好:

/src/Counter.svelte
<style>
  /* 以下只顯示修改的 CSS */
p {
    font-size: 2em;
    font-weight: 600;
  }
</style>

  我們的 App.svelte 當中,也有 <p class='comment'> 的元素存在,然而 Counter.svelte 當中的 CSS 效果卻無法影響到 App.svelte<p class='comment'>。這就是 Svelte 所創造出來,存在平行宇宙不互相干擾的 CSS 了。
  最後提醒一下,Svelte 元件在命名的時候,通常以大寫的英文字母開頭,如我們的 App.svelteCounter.svelte,用以和一般的 HTML 元素做區隔。那麼今天的內容就到這了,謝謝大家!


上一篇
第 05 天:Svelte 中的 HTML
下一篇
第 07 天:Svelte 中的 Javascript:賦值
系列文
了不起的 Svelte30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言