記得剛遇見css的時候,咱像是見了美人兒一樣,簡直是愛不釋手啊,簡簡單單寫幾行算不上代碼的代碼,就能做出這麼漂亮的東西,這也成了咱前端之路的最初動力。然而,隨著項目體量和頁面複雜度的增加,咱很快就發現這美人兒非但不漂亮,而且缺胳膊少腿兒的。 ...
記得剛遇見css的時候,咱像是見了美人兒一樣,簡直是愛不釋手啊,簡簡單單寫幾行算不上代碼的代碼,就能做出這麼漂亮的東西,這也成了咱前端之路的最初動力。
然而,隨著項目體量和頁面複雜度的增加,咱很快就發現這美人兒非但不漂亮,而且缺胳膊少腿兒的:
- 缺少模塊系統。模塊系統是軟體工程化的基石,CSS 的這個缺陷對前端項目的工程化管理造成了很大阻力,導致開發大型應用時編碼和維護都異常困難。js 一開始也沒有模塊系統,後來各種輪子頻出,什麼CMD,AMD,UMD全蹦出來了,亂哄哄的,好在 ES6 從語言層面引入了模塊系統才終結了這種亂象,以後 js 的模塊化終於可以統一了。css 你怎麼不好好向你的好基友 js 學習呢,人家都有了,你還傻了吧唧的一點動靜都沒有。
- 沒有變數機制。這對控制多個地方會引用到的屬性值很不方便。比如一個顏色值,頁面好多地方用到,設計MM突然心血來潮把這個值換成了另一個顏色,咱們怎麼辦,ctrl+f 全局替換?萬一換掉了不該換的,或者漏掉了幾個怎麼辦。
- 嵌套的層級寫法非常蛋疼。經常會出現
這種看起來很不爽,寫起來更不爽的寫法。這是程式員最不能忍受的——重覆。.page .content .left-side .profile .name{};
.page .content .left-side .profile .age{}; - 復用困難。復用是軟體工程的核心思想,css 不僅沒提供模塊系統,而且巧妙地避開了工程化的諸多實踐。更加覺得 css 這門語言設計的跟鬧著玩兒似的。
- blabla.. 其它的都不是很嚴重啦。
有需求就會有市場,Sass(Syntactically Awesome Style Sheets) 應運而生。
老規矩,先來看看 Sass 官網的原話:
Sass 是世界上最成熟、最穩定、最強大的專業級 CSS 擴展語言!這貨還真是一點也不謙虛,“最成熟,最穩定,最強大”。
通過這幾年項目中的實際運用,咱發現這句簡短霸氣的描述其實並沒有絲毫的浮誇,Sass 的確厲害,完全可以 hold 住這三個“最”字,實至名歸。
什麼是預處理器預處理器是在程式源文件被編譯之前根據預處理指令對程式源文件進行處理的程式。說白了,預處理器只不過是一個文本替換工具而已。CSS 預處理器則是通過將有特殊語法和指令的源代碼處理成瀏覽器可使用的 CSS 文件的程式。是 Sass 還是 SCSS?SCSS 是 Sass 3 引入的新語法,語法上完全相容原生 CSS,功能上完全繼承 Sass,可以說是 CSS 和 Sass 的完美融合。SCSS 之於 Sass 猶如 CSS3 之於 CSS,ES6 之於 JS。所以別糾結,其實是一個東西啦。
接下來就細數 Sass 帶給咱們的四大實用特性,想必你一定已經使用過它們中的一個或幾個。
想想之前咱們是怎樣寫原生 css 的:
.page .content .left-side .profile .name{
font-size: 2rem;
}
.page .content .left-side .profile .age{
color: red;
}
現在使用 scss 可以這樣寫:
.page{
.content{
.left-side{
.profile{
.name{
font-size: 2rem;
}
.age{
color: red;
}
}
}
}
}
編譯後
.page .content .left-side .profile .name{font-size: 2rem;}
.page .content .left-side .profile .age{color: red;}
這種嵌套寫法的好處是顯然的:
- 結構清晰簡潔,並且可與 html 文檔結構對應起來;
- 減少了大量冗餘重覆的選擇器編碼;
變數一直是所有編程語言的標準配置。然而 CSS 就沒有,再次證明 CSS 可能是一門假語言。好在 Sass 補上了這個短板。
沒有變數之前的代碼(這裡以定義一系列表示成功風格的樣式組件為例):
.success-bg{
background: #dff0d8;
}
.success-panel{
.panel-heading{
background: #dff0d8;
}
.panel-body{
border: 1px solid #dff0d8;
}
}
使用了變數後的代碼:
$success-color: #dff0d8;
.success-bg{
background: $success-color;
}
.success-panel{
.panel-heading{
background: $success-color;
}
.panel-body{
border: 1px solid $success-color;
}
}
使用變數的好處是顯而易見的:
- 方便了多人協同作戰,將頻繁使用的屬性值定義成變數放在單獨的文件里,各個開發人員可方便引用而不必再關註這些小細節,有語義的變數名使用起來也要比單調的CSS值容易的多;
- 極大地增強了代碼的可維護性,便於局部和全局的樣式風格統一控制;
模塊化是軟體工程的第一要務,是大型項目的必需建築。軟體工程的主要目標就是控制複雜度,這也正是模塊化的目的。通過將一個大型複雜的工程拆解成一個個的小模塊,使得校驗、調試、測試都輕而易舉。
CSS原生的 @import 提供了一個並沒有卵用的假模塊系統。Sass 對 @import 進行了拓展,實現了一個真正意義上甚至功能更強大的模塊系統。Sass 選擇對 @import 進行擴展,而不是新建一個指令,可見 import 這個關鍵字的語義之強,JavaScript 模塊系統的關鍵字也是 import。
沒有模塊系統之前:
<!-- index.html -->
<link rel="stylesheet" href="/your/site/common.css">
<link rel="stylesheet" href="/your/site/popup.css">
<link rel="stylesheet" href="/your/site/module_a.css">
<link rel="stylesheet" href="/your/site/site.css">
有了模塊系統之後:
/* site.scss */
@import "common";
@import "popup";
@import "module_a";
<!-- index.html -->
<link rel="stylesheet" href="/your/site/site.css">
好處嘛自然不用多說了:
- 增刪改模塊以後是 css 自己家的事,別麻煩別人,不用再去動 html 了吼;
- 模塊系統使得項目並不會隨著業務複雜度增加而變得更加複雜。增加功能時只需要橫向擴展就行了,不會縱向延伸,從而能始終保證每個模塊完整而簡單;
混合(mixin)特別類似於 JavaScript 中的函數,然而 Sass 提供了用於表達式計算的 @function 函數指令,這裡就不好這麼類比了。但其實就是這麼個東西,調用的時候會返回一段樣式。
比如下麵一段存在重覆樣式的代碼。
復用之前:
.description{
color: red;
border: 1px solid #e3e3e3;
border-radius: 2px;
}
.article{
color: #444;
border: 1px solid #e3e3e3;
border-radius: 2px;
}
稍作優化:
.description, .article{
border: 1px solid #e3e3e3;
border-radius: 2px;
}
.description{
color: red;
}
.article{
color: #444;
}
似乎不錯,但是之後再新加類似樣式時,
.description, .article, .style01, .style02{
border: 1px solid #e3e3e3;
border-radius: 2px;
}
.
.
.
.style01{}
.style02{}
每次都要改兩個地方,很麻煩,很容易漏,尤其是將通用樣式分離出來的話更容易出錯。
再做優化:
.grey-border-radius{
border: 1px solid #e3e3e3;
border-radius: 2px;
}
.description{
color: red;
}
.article{
color: #444;
}
似乎好了一點,但這樣的話,html 每個使用的標簽都需要多加上一個 .grey-border-radius 類。很顯然這是多餘的。這種做法可以說是“湊合”。
使用 Sass 復用之後:
@mixin grey-border-radius{
border: 1px solid #e3e3e3;
border-radius: 2px;
}
.description{
@include grey-border-radius;
color: red;
}
.article{
@include grey-border-radius;
color: #444;
}
編譯後的 css 輸出:
.description {
border: 1px solid #e3e3e3;
border-radius: 2px;
color: red;
}
.article {
border: 1px solid #e3e3e3;
border-radius: 2px;
color: #444;
}
看到了吧,這種做法簡直“完美”:
- 抽離公共的樣式片段,便於多處復用;
- 將公共的樣式片段放在單獨的文件里,便於項目的多個文件復用;
- 對 html 的使用沒有任何要求,css 自己家的事自己關起門來解決,絕不麻煩別人;
如果熟練合理地運用上面的四大特性,你已經是CSS代碼工程化方面的磚家了,所寫出來的代碼必是清晰易維護的。Sass 提供了更多的功能,但對普通開發者來講,上面的四點只要使用熟練,已經完全夠用了,其他的可看可不看。下麵提供的功能希望大家慎用,有的是出於性能考慮,有的則是從開發維護的角度考慮。尤其不要為了秀技術而去使用它們,過猶不及,事緩則圓,此為中庸之道。
語義層的復用——繼承機制
繼承是面向對象程式設計的三大特性之一,這也是為什麼說它是語義層復用的原因。你可以說一個錯誤信息框繼承了一個信息框,而不能說一個錯誤信息框繼承了一個灰色圓角,雖然也是可以強行這麼說,但難免有些彆扭哈哈。
比如說下麵定義一組信息框的樣式,包括預設,成功和錯誤的樣式。
使用繼承之前:
.msg{
border: 1px solid #e3e3e3;
background: #dff0d8;
}
.msg-success{
color: #4cae4c;
}
.msg-error{
color: #d43f3a;
}
同樣是上面說到的問題,編寫 html 時每個使用的標簽都需要多加上一個 .msg 類,很多餘。
使用繼承之後:
.msg{
border: 1px solid #e3e3e3;
background: #dff0d8;
}
.msg-success{
@extend .msg;
color: #4cae4c;
}
.msg-error{
@extend .msg;
color: #d43f3a;
}
編譯後
.msg, .msg-success, .msg-error {
border: 1px solid #e3e3e3;
background: #dff0d8;
}
.msg-success {
color: #4cae4c;
}
.msg-error {
color: #d43f3a;
}
可以看出,上面的效果使用混合(mixin)也可以完成。但不同的是:繼承拷貝的是選擇器,而混合(mixin)拷貝的是樣式片段。
使用混合(mixin)還是繼承(extend)?你肯定以為既然繼承拷貝的是選擇器,而混合拷貝的是大段的樣式,那當然是優先選擇繼承了。然而恰恰相反,推薦做法是儘可能使用混合(mixin),具體原因 戳這裡。用於複雜計算的函數
這個功能主要用於值的計算,和 JavaScript 中的函數類似。
比如移動端開發時可以封裝成一個函數用於把 px 轉成 rem。
$baseFontSize: 20;
@function px2rem($val) {
@return $val/$baseFontSize + rem;
}
.big-text{
font-size: px2rem(30);
}
編譯後:
.big-text {
font-size: 1.5rem;
}
這樣在拿到設計MM給的視覺稿之後就可以直接使用 px 進行測量使用了。
完善的控制流
控制流即程式語言中的 if/else
,for
,while
等控制語句。Sass 同樣提供了指令實現:
@if
@for
@each
@while
它們通常配合 @function
指令使用,然而功能雖強,卻不常用到。畢竟樣式表的功用主要是描述頁面樣式,而不是提供更多控制。因此在這裡不展開研究,感性趣的戳這裡。
Sass 完美彌補了上面原生 CSS 暴露的幾個短板,同時新語法 SCSS 使 CSS 開發者可以無縫過渡,是 CSS 預處理器中當之無愧的佼佼者。使用 Sass 容易編寫出結構清晰,可復用,易維護的工程樣式文件,這正是工程化的期望。這麼好的東西,速速用起來。
本文主要參考了 Sass 中文網。