1# CSS無法遞歸式定義
CSS語法不支持遞歸定義的表達式,所以你沒有辦法用一個語句定義一個啟發式的規則。
比如這樣的需求:“.w
后面跟着一個數字,這個數字代表着width為百分之多少”(bootstrap的柵格系統就包含12種相對父級寬度的類定義)。
盡管完全是一個規則里定義出的,但你只能這樣寫CSS:
.w1 { width: 1% }
.w2 { width: 2% }
/** .w3 ... ... .w99 **/.w100 { width: 100% }
這樣將造成很大的冗余,修改費時費力。但如果預編譯CSS,就非常簡單了:
@maxnumber : 100 ;
.makeWidthRules(@number) when(@number <= @maxnumber ){
.w@{number}{ width: 1% * @number ;
}
.makeWidthRules(@number + 1) ;
}
.makeWidthRules(1) ;
2# CSS的mixin式復用性支持不夠
使用純CSS,我們可以抽象出一些常用的布局CSS屬性組合,通過CSS的類組合來達成常見的mixin式復用。
比如這樣:
<style>.tc { text-align: center; }.m { margin-left: auto; margin-right: auto; }.w50p { width: 50%; }.db { display: block; }</style><div class="tc m w50p"><img class="db"></div>
這種方案有幾個問題:
- 頁面重構時,需要頻繁修改class name;
這個問題在后端人員掌握着視圖層的時候格外突出,前后端耗費很多溝通成本。 - 要約束上下文的時候非常無力
比如“只有在ul
下面的img.db
允許是display:block
”的規則,寫成ul img.db { display: block; }
就完全跑偏了——它違背了你創建這個.db
類時的本意,造成了代碼的可讀性和可維護性下降。如果你要改動規則,需要同時修改HTML和CSS,也可能造成新的樣式問題。
如果我們想要建立一種代碼風格,只允許CSS Class代表UI模塊的抽象——改動樣式時不至於通知后端改模板——我們就要將上面這個例子的tc m w50p
換用一個有實際語義的類名如headwrap
,然后在CSS內部實現mixin。
——而這正是CSS的短板,CSS體系內的用法只有復制粘貼。
至於如何用預編譯語言做mixin,一個非常好的SASS示例由
在 里給出,容我摘錄一小段:.btn-standout {
@extend .btn;
@extend .btn-block;
@media (max-width: $screen-xs-max) {
@include button-size(
$padding-large-vertical,
$padding-large-horizontal,
$font-size-large,
$line-height-large,
$border-radius-large
);
}
&.sell {
@extend .btn-primary;
}
}
3# 預編譯可緩解多瀏覽器兼容造成的冗余
進入CSS3的時代,舊式CSS hack如filter
,新式兼容前綴如-webkit-
等,都是冗余,修改的時候也需要修改多處,不容易維護。
比如對rgba背景的兼容:
.bg { filter: progid:DXImageTransform.Microsoft.gradient(startcolorstr=#ccff825b,endcolorstr=#ccff825b); }:root .bg { -ms-filter: none; background: rgba(255,130,91,0.8) }
在LESS里面,寫個函數就能解決,多次復用也不需要看到如此之多的hack:
.rgbaBG(@c , @a){
@rgba : rgba(red(@c),green(@c),blue(@c),@a);
@shim : argb(@rgba) ;
filter: ~"progid:DXImageTransform.Microsoft.gradient(startcolorstr=@{shim},endcolorstr=@{shim})" ;
:root & {
-ms-filter: none ;
background: @rgba ;
}
}
.bg {
.rgbaBG(#ff825b, 0.8) ;
}
此外,使用LESS時,可以很方便地使用base64 data uri的方案。不需要直接面臨在CSS中一大坨字符:
.bg { background: data-uri('../data/image.jpg'); }
SASS的類似方案見評論。
N# 預編譯不是萬金油
預編譯不是萬金油,CSS的好處在於簡便、隨時隨地被使用和調試。預編譯CSS步驟的加入,讓我們開發工作流中多了一個環節,調試也變得更麻煩了。
舉個例子:原先我們只需要在chrome/firebug里面找到相應的選擇器,如.popup .popup-wrap .head
,源文件里面ctrl+F查找.popup .popup-wrap .head
就可以快速定位語句。現在我們無法直接在預編譯文件中查找,而需要尋找上下文,因為它在LESS中通常是這樣被定義的:
.popup {
.popup-wrap {
.head { }
}
}
更大的問題在於,預編譯很容易造成后代選擇器的濫用。
曾經有一個觀點是預編譯可以解決樣式覆寫的問題,而我覺得,正是預編譯語言模糊了樣式覆寫的問題,而導致要解決樣式相互覆寫的問題時,問題已經變得規模龐大而難以解決。
舉個極端的例子:
.popup {
font-size: 12px;
a { font-size: 13px; }
.head { font-size: 18px; }
}
.informative {
font-size: 14px;
.head { font-size: 16px; }
}
如果我有這么一個文檔結構.popup.informative > .head > a
,需要a
的font-size
為17px
,你能快速想明白怎么改嗎?疊羅漢式地再疊一層?還是再糊一層牆皮,加一行樣式?還是干脆用!important
轟炸一番?
因此,實際項目中衡量預編譯方案時,還是得想想,比起帶來的額外維護開銷,預編譯有沒有解決更大的麻煩。