她就這麼大辣辣地走了進來,以一種檢視財產的姿態,將房間裡的 CSS 選擇器都看了個遍,讓我不禁思索她是否會干擾到他們。當我這麼問她時,她笑了出來,強調似的把我的問題重複了一遍,然後告訴我她不會。
~節錄自《The Great Svelte:第二章》
昨天的文章內容,我們說明了 Svelte 元件是如何擴展 HTML 的功能,藉由在 HTML 當中使用大括弧 {}
展開 Javascript 領域,使得 HTML 呈現的內容可以隨著 Javascript 的變化做出調整,進而讓我們能夠更輕鬆地寫出互動式的前端專案。
今天就來講講 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 的修飾,看起來稍嫌尷尬呢?
圖一、尷尬又不失禮貌的 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 元素囉!
圖二、CSS 修飾過後的 HTML
這樣看起來,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.svelte
、Counter.svelte
,用以和一般的 HTML 元素做區隔。那麼今天的內容就到這了,謝謝大家!