可以往前點到 Day 3 那篇,在拆解 .vue的三個區塊時,<style>後面有一個 “scoped”。
為什麼要在 <style> 後面加上一個 scoped 呢?這就是今天的第一個重點,我們要來介紹 scoped 這個屬性,說明它在 Vue 裡有什麼功能。
簡單來說,
scoped可以在我們建立新的元件時,避免一樣名稱的Class相互影響,造成全域樣式汙染。
<button> ……在src/components/ 底下新增一個子元件:MemoryCard.vue 。
(未來要拿來展示每一顆光珠故事的小卡片)
輸入以下程式碼:
<template>
<div class="card">
<p>這是一顆裝著美好回憶的光珠</p>
<button class="action-btn">查看當天回憶</button>
</div>
</template>
<style>
/* 故意不加 scoped! */
button {
background-color: #ff9800;
color: white;
border: none;
border-radius: 20px;
padding: 8px 16px;
font-weight: bold;
}
p {
color: #ffb74d;
}
</style>
<script setup>
import { ref } from 'vue'
// 1. 引入子元件
import MemoryCard from './components/MemoryCard.vue'
const count = ref(0)
function addGem() {
count.value++
console.log(count.value)
}
</script>
<template>
<p>已採集 {{ count }} 顆光珠</p>
<button @click="addGem">採集</button>
<!-- 2. 掛載子元件 -->
<MemoryCard />
</template>


明明我們是將橘色字和橘色橢圓形按鈕寫在MemoryCard.vue 裡面,為什麼連採集的區塊都變成這種樣式?
原因就是我們沒有在子元件的<style>後面加上scoped!這會使子元件裡寫的樣式外洩成「全域樣式(Global CSS)」,無差別地將樣式套到整個專案上!
瀏覽器在讀取 CSS 時,預設並沒有「元件」的概念。當我們在 MemoryCard.vue 寫下 button 和 p 卻沒有加上 scoped 時,瀏覽器會把這些樣式當成全站通用的規則。
結果就是:瀏覽器只要在畫面上看到任何 <button> 或 <p>(包含 App.vue 原本白色方形的採集按鈕與文字),都會強制替它們套上橘色與橢圓形外觀。
<style> 後面加上 scoped,不讓元件樣式互相影響:<style scoped>
/* 加上 scoped 後,這裡的樣式就只會對 MemoryCard.vue 生效 */
button {
background-color: #ff9800;
/* ... */
}
</style>
到瀏覽器看更改後的畫面就是正常的樣式了,如下圖:

💡底層運作的小秘密
加上 scoped 後,Vue 在編譯時會偷偷替這個元件的所有 HTML 標籤加上一串唯一的識別碼(例如 data-v-7ba5bd90),並把 CSS 選擇器改寫成 button[data-v-7ba5bd90],這就是樣式不會外洩的真正原因!
看完了樣式污染的實驗,我們就知道 <style scoped> 有多重要了!接下來請先將剛剛在 App.vue 引入的 MemoryCard 拿掉(我們在後面的篇章會再用到它),現在要回過頭來專心設計專案主角——採集瓶的外觀。
今天只會稍微寫出瓶子外觀,細節的設計(例如陰影、玻璃質感)就等之後再慢慢調整。
我想要採集瓶開口有一個軟木塞,在我加入光珠時,軟木塞會先被打開,在讓光珠緩緩落進瓶中,所以今天要做的部分有兩個:
/* 軟木塞 */
.cork {
width: 40px;
height: 12px;
background-color: #a07855;
border-radius: 3px;
}
/* 瓶身 */
.bottle {
width: 160px;
height: 220px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 12px 12px 40px 40px; /* 上小下大的圓角就是瓶子 */
display: flex;
align-items: center;
justify-content: center;
color: #6e7681;
}
如果想要完整App.vue程式碼可以看下面這段,內容包含一些置中、結構細項。
App.vue 完整程式碼
<script setup>
import { ref } from 'vue'
const count = ref(0)
function addGem() {
count.value++
console.log(count.value)
}
</script>
<template>
<main class="bottle-app">
<h1>微光採集瓶</h1>
<p class="subtitle">在每一天的最後,採集一道微小的美好</p>
<!-- 瓶子結構 -->
<div class="bottle-wrapper">
<div class="cork"></div>
<div class="bottle">
<span>瓶子目前是空的</span>
</div>
</div>
<!-- 原本的計數與採集按鈕 -->
<p>已採集 {{ count }} 顆光珠</p>
<button @click="addGem">採集</button>
</main>
</template>
<style scoped>
/* 頁面置中對齊 */
.bottle-app {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
text-align: center;
gap: 12px;
}
/* 瓶子的樣式 */
.bottle-wrapper {
display: flex;
flex-direction: column;
align-items: center;
margin: 16px 0;
}
/* 軟木塞:棕色小方塊 */
.cork {
width: 40px;
height: 12px;
background-color: #a07855;
border-radius: 3px;
}
/* 瓶身:透明帶白邊的長方圓角框 */
.bottle {
width: 160px;
height: 220px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 12px 12px 40px 40px; /* 上小下大的圓角勾出瓶子輪廓 */
display: flex;
align-items: center;
justify-content: center;
color: #6e7681;
font-size: 0.85rem;
}
</style>

今天的文章就到這邊結束啦~希望讀者在看完後可以對 scoped 有詳細了解,也可以自己變更一些程式碼做出你更喜歡的瓶身造型喔!
Day 6 要學習動態綁定(v-bind),讓今天看起來只是由線條組成的玻璃瓶可以擁有動態樣式,變得更吸睛!