CSS 在實務上常會遇到重複 Property,為了方便日後維護,應盡量避免重複,本文介紹兩種重構方式,各有其優缺點。
Version
macOS Catalina 10.15.5
WebStorm 2020.1.3
Vue 2.6.11
CSS 3
Duplicate CSS
上方顯示 book,下方顯示 category,雖然資料並不同,但顯示結果類似,也就是其 CSS style 相似,重複的 property 甚多。
<template>
<div>
<div class="book-list">
<a href="#" class="book-item">FP in JavaScript</a>
<a href="#" class="book-item">RxJS in Action</a>
<a href="#" class="book-item">Speaking JavaScript</a>
</div>
<div class="category-list">
<a href="#" class="category-item">FP</a>
<a href="#" class="category-item">FRP</a>
<a href="#" class="category-item">JavaScript</a>
</div>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style scoped>
.book-list {
border: 1px solid #ccc;
padding: 20px;
margin-top: 10px;
}
.book-item {
color: red;
text-decoration: none;
display: list-item;
}
.category-list {
border: 1px solid #ccc;
padding: 20px;
margin-top: 20px;
}
.category-item {
color: blue;
text-decoration: none;
display: list-item;
}
</style>
第 3 行
<div class="book-list">
<a href="#" class="book-item">FP in JavaScript</a>
<a href="#" class="book-item">RxJS in Action</a>
<a href="#" class="book-item">Speaking JavaScript</a>
</div>
上方顯示 book,<div>
使用 book-list
class,而 <a>
使用 book-item
class。
第 9 行
<div class="category-list">
<a href="#" class="category-item">FP</a>
<a href="#" class="category-item">FRP</a>
<a href="#" class="category-item">JavaScript</a>
</div>
下方顯示 category,<div>
使用 category-list
class,而 <a>
使用 category-item
class。
24 行
.book-list {
border: 1px solid #ccc;
padding: 20px;
margin-top: 10px;
}
35 行
.category-list {
border: 1px solid #ccc;
padding: 20px;
margin-top: 10px;
}
.category-list
除了 margin-top
不同外,其餘與 .category-list
完全一樣。
30 行
.book-item {
color: red;
text-decoration: none;
display: list-item;
}
42 行
.category-item {
color: blue;
text-decoration: none;
display: list-item;
}
.book-item
與 .category-item
也類似,僅有 color
不同。
Class Selector
<template>
<div>
<div class="list book-list">
<a href="#" class="item">FP in JavaScript</a>
<a href="#" class="item">RxJS in Action</a>
<a href="#" class="item">Speaking JavaScript</a>
</div>
<div class="list category-list">
<a href="#" class="item">FP</a>
<a href="#" class="item">FRP</a>
<a href="#" class="item">JavaScript</a>
</div>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style scoped>
.list {
border: 1px solid #ccc;
padding: 20px;
}
.list.book-list {
margin-top: 10px;
}
.list.category-list {
margin-top: 20px;
}
.item {
text-decoration: none;
display: list-item;
}
.book-list .item {
color: red;
}
.category-list .item {
color: blue;
}
</style>
24 行
.list {
border: 1px solid #ccc;
padding: 20px;
}
將 .book-list
與 .category-list
共同的 property 抽成 .list
。
37 行
.item {
text-decoration: none;
display: list-item;
}
將 .book-item
與 .category-item
共同的 property 抽成 .item
。
第 3 行
<div class="list book-list">
<a href="#" class="item">FP in JavaScript</a>
<a href="#" class="item">RxJS in Action</a>
<a href="#" class="item">Speaking JavaScript</a>
</div>
在 HTML 一樣使用 .book-list
,但沒使用 .book-item
。
這裡的 .book-list
只是為了 class selector 辨識用。
29 行
.list.book-list {
margin-top: 10px;
}
.list.category-list {
margin-top: 20px;
}
以 multiple class selector 辨識出是 book list 或 category list,然後補上 CSS 特殊部分。
42 行
.book-list .item {
color: red;
}
.category-list .item {
color: blue;
}
以 descendant combinator 辨識出 book item 或 category item,然後補上 CSS 特殊部分。
Extract Class
<template>
<div>
<div class="list book-list">
<a href="#" class="item book-item">FP in JavaScript</a>
<a href="#" class="item book-item">RxJS in Action</a>
<a href="#" class="item book-item">Speaking JavaScript</a>
</div>
<div class="list category-list">
<a href="#" class="item category-item">FP</a>
<a href="#" class="item category-item">FRP</a>
<a href="#" class="item category-item">JavaScript</a>
</div>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style scoped>
.list {
border: 1px solid #ccc;
padding: 20px;
}
.book-list {
margin-top: 10px;
}
.category-list {
margin-top: 20px;
}
.item {
text-decoration: none;
display: list-item;
}
.book-item {
color: red;
}
.category-item {
color: blue;
}
</style>
24 行
.list {
border: 1px solid #ccc;
padding: 20px;
}
一樣將 .book-list
與 .category-list
共同的 property 抽成 .list
。
29 行
.book-list {
margin-top: 10px;
}
.category-list {
margin-top: 20px;
}
.book-list
與 .category-list
則只留下相異部分。
37 行
.item {
text-decoration: none;
display: list-item;
}
一樣將 .book-item
與 .category-item
共同的 property 抽成 .item
。
42 行
.book-item {
color: red;
}
.category-item {
color: blue;
}
.book-item
與 .category-item
則只留下相異部分。
第 3 行
<div class="list book-list">
由於原本 .book-list
拆成 .list
與 .book-list
,因此要改成組合 .list
與 .book-list
。
第 4 行
<a href="#" class="item book-item">FP in JavaScript</a>
由於原本 .book-item
拆成 .item
與 .book-item
,因此要改成組合 .item
與 .book-item
。
category-list
與 category-item
也同理。
Conclusion
- 第一種寫法充分發揮 class selector 特性,使用了 multiple class selector 與 descendant combinator,class 只是用來給 selector 辨識使用,優點是 class 較少,缺點是必須很熟悉 class selector 各種 syntax
- 第二種寫法充滿 FP 思維,抽出共用 class,並實作出特殊 class,並沒有使用任何特殊 syntax,最後在 HTML 組合使用,優點是非常靈活,且只用了最基礎 class selector,缺點是 class 較多
- 實務上常看到第一種寫法,這算是 CSS 傳統風格,在 CSS 透過靈活使用 class selector 完成目標;第二種則屬於 Function CSS 風格,只使用最基本 CSS,由 HTML 組合 class 完成目標