前言
本文主要描述的組織css的問題,文章內容整理自《前端架構設計》的讀書筆記。
繼承與特性之爭
h2{
}
#sidebar h2{
background:red;
}
#sidebar .calendar h2{
background:green;
}
複製程式碼
上面這樣寫樣式有很大的問題,主要是我們在樣式書寫時預設的繼承到了之前的樣式,但在特殊化的樣式時,為了讓自己的樣式生效,又要新寫樣式進行覆蓋。
所以列舉一下可能的問題是下面的:
- 選擇器的優先順序:選擇合適的優先順序,查詢效率高,方便其他人樣式覆蓋的需求
- 顏色重置:要恢復到原來的顏色,需要進行不斷的重置或者賦值
- 位置依賴:如果我們的樣式移到其他位置,那麼樣式程式碼就會失效,因為嚴格依賴dom的結構和順序,不能很好的解耦
- 多重繼承:最終元素的樣式可能是層層巢狀得到的最終樣式程式碼,非常不可複用,當改變主體的樣式時,子元素都會受到影響
- 深層巢狀:效率非常低
現代化的模組方案
在之前的章節中,我們講到了css不同的模組化方案,那麼對於上面描述的問題,其實可以很簡化的去解決。
案例程式碼
你根據以上的需求按照模組的方案和命名,去除各種依賴,最大程度使用class命名方式,減少繼承,更大程度的使用獨立樣式程式碼塊,輕鬆解決了以上的問題。
<h2 class="content_title"></h2>
<div class="sidebar">
<h2 class="content_title--reversed"></h2>
<div class="calendar">
<h2 class="calendar_title"></h2>
</div>
</div>
複製程式碼
// 元件資料夾
.content_title{}
// 不用恢復重置樣式
.contitle_title--reverse{}
// 特殊元件的title定義
.calendar_title{
}
複製程式碼
單一職責原則
比如我們在定義標題的時候,可能有兩個模組都用到了標題。
<div class="calendar">
<h2 class="primary-title"></h2>
</div>
<div class="blog">
<h2 class="primary-title"></h2>
</div>
<style>
.primary-title{
}
</style>
複製程式碼
可能的問題,是我們需要在日曆或者部落格的時候需要去修改這個樣式,於是你的樣式可能變成這樣的。
.primary-title{
}
.blog .primary-title{}
複製程式碼
這樣的問題是會導致選擇器過多,於是我們按照bem進一步優化。
.calendar_header{}
.blog_header{}
複製程式碼
這樣維護之後,每個樣式塊都只負責自己的內容,除了導致程式碼的重複之外沒有任何壞處。好處是如果專案嚴格執行單一職責方式,當我們改動任何程式碼的時候,不會擔心對全域性造成的影響。那麼重複的程式碼怎麼解決,這個其實webpack的部分和gzip的部分已經能把我們的程式碼進行優化了。
延伸思考:如果壓縮打包的角度可以去掉重複程式碼的部分,那麼更多角度考慮程式碼的可持續、可維護就好了。
單一樣式來源
簡單來講,就是你的樣式程式碼應該來源於儘可能少的元件,最好是維護在一個class中,保證整個樣式程式碼是可追溯的,也可以預想效果的。
案例
<div class="blog">
<div class="blog-header"></div>
</div>
<div class="calendar">
<div class="calendar-header"></div>
</div>
複製程式碼
/* calendar css*/
.calendar-header{
}
/* blog css*/
.blog-header{
}
.blog .blog-header{}
複製程式碼
以blog進行title的限定,主要是限制讓字號小一點,這種的主要問題是當專案積累下來,會有很多樣式程式碼散落在各個元件,這樣導致不可追溯。(補充個常識,以父元素為什麼進行修飾子元素,這個稱為樣式上下文)
那麼建議的方式是將散落在各處的程式碼完全控制在一個元件內。
元件修飾符
雖然單一元件的原則讓我們對元件的樣式程式碼封裝的很好,但還是有一些特殊的需求,那我們如何設計這方面的程式碼呢?通過皮膚或者子模組來實現,也可以稱為元件修飾符。
比如我們針對日曆的元件,我們需要追加一個特殊的樣式,可以這樣實現。
.calendar-header{
}
.calendar--nested .calendar-header{
}
複製程式碼
通過這樣的方式,我們不但可以實現皮膚的需求,也可以實現對上下文的解耦,我們只要關注元件需要什麼樣的特殊樣式,進行元件修飾即可,而不用依賴耦合在父容器裡。
element-ui的css
以下以element的2.4.11版本為例,根據其api文件以及原始碼的角度為大家分析element-ui是如何解決這些問題的。
el-button分析
-
api設計規範:可以看到button的暴露的api主要是基於屬性的
引數 說明 型別 可選值 預設值 size 尺寸 string medium / small / mini — type 型別 string primary / success / warning / danger / info / text — plain 是否樸素按鈕 boolean — false round 是否圓角按鈕 boolean — false circle 是否圓形按鈕 boolean — false loading 是否載入中狀態 boolean — false disabled 是否禁用狀態 boolean — false icon 圖示類名 string — — autofocus 是否預設聚焦 boolean — false native-type 原生 type 屬性 string button / submit / reset button -
class的設計,雖然元件的設計是基於屬性的,但實際不同的屬性最終都是表現為class的。那麼基於這樣的角度去設計樣式是element-ui的首創或者是vue元件的創新麼?其實早在bootstrap裡就是這樣設計的。
class 說明 el-button-group 按鈕組的樣式,作為外部容器,不對el-button產生任何樣式程式碼,但對其內含有的el-button會產生垂直居中的效果,還有向右的間距, .el-button-group .el-button--primary:first-child{}
el-button el-button--default 基本樣式,修飾符,生效規則el-button--default也是直接定義其樣式規則,不依賴於el-button el-button el-button--medium el-button--medium 修飾符中直接定義好尺寸的全部程式碼 el-button el-button--default is-circle 基本樣式,smacss狀態樣式 ,其中is-circle包含這個原型的全部樣式,其生效的條件是與el-button同時生效,is-disabled同理 icon="el-icon-edit" 帶icon的部分不在button裡做特殊樣式,其樣式屬於基本樣式,但是dom結構是通過元件進行新增的,那麼其圖示的字號來源於哪裡呢?來源於el-button的基本樣式14px,所以這部分進行了解耦,不用單獨設定 -
元件內如何根據屬性進行返回對應的class.
class="el-button"
// 可以看到其根據傳入的各個type屬性分別將class追加到class陣列之中
:class="[type ? 'el-button--' + type : '',buttonSize ? 'el-button--' + buttonSize : '',{'is-disabled': buttonDisabled,'is-loading': loading,'is-plain': plain,'is-round': round,'is-circle': circle}]"
//根據為loading 顯示出固定的loading圖示
<i class="el-icon-loading" v-if="loading"></i>
// 根據傳入的icon以及沒有loading顯示出對應type的icon
<i :class="icon" v-if="icon && !loading"></i>
複製程式碼
輕度定製的element
經常有場景我們需要引入element-ui之後需要對其ui進行皮膚化的開發樣式,雖然element-ui有暴露其對應的元件樣式,我們也可以進行對應的詳細的原始碼的fork並開發,但在大多數中小公司其實不用小題大做。我們只需要根據自己的情況,針對一樣的樣式進行全域性樣式覆蓋即可。那麼,我們專案中針對按鈕的皮膚化改變會是這樣的。
// 定義variables.scss的主題變數
// 主題色相關變數
$color-thin:rgba(60,191,196,0.1);
$color:rgba(60,191,196,1);
// customer-element.scss 主要定義已使用餓了麼元件樣式的修改
@import './variables';
// 一個按鈕樣式的全域性覆蓋
.el-button--primary{
&:hover,&:focus{
background:$color;
border-color:$color;
}
background:$color;
border-color:$color;
}
複製程式碼
bootstrap的css
我們同樣也是分析bootstrap裡的樣式使用,基本也是使用class拼盤理論的。
<div class="btn-group" role="group" aria-label="...">
<button type="button" class="btn btn-default">Left</button>
<button type="button" class="btn btn-default">Middle</button>
<button type="button" class="btn btn-default">Right</button>
</div>
複製程式碼
button.less樣式檔案節選,可以看到其基本樣式、巢狀內偽類的樣式,button-size的方法類,修飾符的(子模組的)維護方式,這都是值得我們借鑑的科學規範思想。
// Base styles
// --------------------------------------------------
.btn {
display: inline-block;
......
.button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @btn-border-radius-base);
.user-select(none);
&,
&:active,
&.active {
&:focus,
&.focus {
.tab-focus();
}
}
&:hover,
&:focus,
&.focus {
color: @btn-default-color;
text-decoration: none;
}
a& {
&.disabled,
fieldset[disabled] & {
pointer-events: none; // Future-proof disabling of clicks on `<a>` elements
}
}
}
// Alternate buttons
// --------------------------------------------------
.btn-default {
.button-variant(@btn-default-color; @btn-default-bg; @btn-default-border);
}
.btn-primary {
.button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border);
}
複製程式碼
小結
到此為止總結的技巧點如下:當然肯定還有更多的技巧等待你補充
- 分離容器與內容
- 單一樣式來源
- 區分佈局與元件的角色
- 在標記上使用單一的、扁平的標識
- 元件修飾符
通過本文我們瞭解並總結了css程式碼的繼承與特殊化的問題,並通過上面的技巧解決了這樣的痛點,並用來顯著提升自己的程式碼水平。