
作者:守候
文章轉(zhuǎn)載自:https://segmentfault.com/a/1190000011390896
1.前言
還有幾天就到國(guó)慶中秋了,快要放假了,先祝大家節(jié)日快樂!之前寫過 JS 的寫作建議和技巧,那么今天就來聊聊 CSS 吧!
說到 CSS,每一個(gè)網(wǎng)頁都離不開 CSS,但是對(duì)于 CSS,很多開發(fā)者的想法就是,CSS 只要能用來布局,把效果圖排出來就可以了,其它的細(xì)節(jié)或者優(yōu)化,不需要怎么考慮。但是我覺得 CSS 可不只是把頁面的布局完成就是完事的,還需要考慮很多細(xì)節(jié)有優(yōu)化,更不會(huì)像大家想得那么簡(jiǎn)單。在學(xué)習(xí)當(dāng)中,如果發(fā)現(xiàn)什么技巧或者優(yōu)化的點(diǎn),我也會(huì)學(xué)以致用!
那么今天,就分享下我總結(jié)的 CSS 寫作建議和性能優(yōu)化的一些問題!希望能幫讓大家對(duì)神奇的 CSS 有一個(gè)新認(rèn)識(shí)。當(dāng)然,如果大家覺得還有什么其它的建議,歡迎指點(diǎn)!
2.CSS 渲染規(guī)則
首選,關(guān)于CSS 渲染的規(guī)則,大家可能都知道,是從右到左的渲染!如下栗子:
.nav h3 a{font-size: 14px;}
渲染過程大概是:首先找到所有的a,沿著a的父元素查找h3,然后再沿著h3,查找.nav。中途找到了符合匹配規(guī)則的節(jié)點(diǎn)就加入結(jié)果集。如果找到根元素html都沒有匹配,則不再遍歷這條路徑,從下一個(gè)a開始重復(fù)這個(gè)查找匹配(只要頁面上有多個(gè)最右節(jié)點(diǎn)為a)。
3.嵌套層級(jí)不要超過 3 級(jí)
一般情況下,元素的嵌套層級(jí)不能超過 3 級(jí),過度的嵌套會(huì)導(dǎo)致代碼變得臃腫、沉余、復(fù)雜,導(dǎo)致 CSS 文件體積變大,造成性能浪費(fèi),影響渲染的速度,而且過于依賴 HTML 文檔結(jié)構(gòu)。這樣的 CSS 樣式,維護(hù)起來,極度麻煩,如果以后要修改樣式,可能要使用!important覆蓋。
4.樣式重置
這個(gè)我目前保持中立意見,因?yàn)榭粗W(wǎng)上的文章,有些人支持使用樣式重置,有些人不支持使用,誰也說服不了誰。我自己的情況,我有使用樣式重置,但是是比較簡(jiǎn)單的一個(gè)總結(jié),代碼如下!
body,dl,dd,h1,h2,h3,h4,h5,h6,p,form,ol,ul {
margin: 0;
padding: 0;
}
h1, h2, h3, h4, h5, h6 {
font-weight: normal;
}
ol, ul {
list-style: none;
}
h1{
font-size: 24px;
}
h2{
font-size: 20px;
}
h3{
font-size: 18px;
}
h4 {
font-size: 16px;
}
h5{
font-size: 14px;
}
h6{
font-size: 12px;
}
5.樣式級(jí)別
首先,CSS 樣式級(jí)別整理如下:
!important > 行內(nèi)樣式 > ID 樣式 > Class 樣式 > 標(biāo)簽名樣式。
然后有一點(diǎn)要提一下就是,組合選擇器使用權(quán)值會(huì)疊加的。比如 ID 的權(quán)值是100,Class 是10,標(biāo)簽名是 1(其它不清楚了)!那么div.test-class權(quán)值就是 11,div#test就是 101.
比如有一個(gè) div:
<div id="test" class="test-class" style="color:green;"></div>
那么樣式權(quán)值方面就是:
-
div {color: red !improtant;}(大于下面的一切) -
<div id="test" class="test-class" style="color:black;"></div>(大于 111) -
div#test.test-class(111) -
#id.test-class(110) -
div#test(101) -
#test(100) -
div.test-class(11) -
.test-class(10) -
div(1) -
*(小于 1)
6.inline-block 的邊距
不解釋,看圖:

上面幾個(gè)p元素margin和padding都為0,但是還有邊距。這個(gè)的解決方案有兩種:
- 刪除代碼之前的空行空格
帶display:inline-block的元素之前的空行都刪除掉,如下寫法:

- 父元素
font-size設(shè)置為0,這個(gè)直接看圖:


7.圖片要設(shè)置 width 和 height
如果頁面有使用img標(biāo)簽,那么img很建議設(shè)置width和height。目的是為了在網(wǎng)速差或者其它原因加載不出圖片的時(shí)候,保證布局不會(huì)亂。如下栗子,一個(gè)很普通的布局。

但是萬一出現(xiàn)什么情況,圖片加載不出來的話,建議的處理方式是第一種,顯示一張默認(rèn)圖片,即使不顯示默認(rèn)圖片,也讓圖片有一個(gè)占位的作用,保證布局不會(huì)亂!


如果圖片加載不出,img又沒有設(shè)置width和height的話,就會(huì)像下面這樣,布局亂了!

關(guān)于設(shè)置width和height,我順便說幾點(diǎn):
- PC 站,建議在
img標(biāo)簽的屬性設(shè)置width和height。這樣避免加載不出 CSS 而錯(cuò)位; - 手機(jī)站,建議用 CSS 設(shè)置
img的width和height,因?yàn)槭謾C(jī)站要做適配,在屬性設(shè)置width和height不靈活,比如使用rem布局,在屬性那里設(shè)置不了width和height; - 如果圖片不固定,但是有一個(gè)
max-width和max-height,那么建議在img的父元素設(shè)置width和height。img根據(jù)父元素的width和height設(shè)置max-width和max-height。
8.任意元素垂直居中
這里只放圖,不解釋:
8-1. table-cell

8-2. flex

8-3. position,transform

8-4. position,margin

這個(gè)方式不推薦使用,因?yàn)檫@個(gè)寫法,.div2的寬高必須要設(shè)置,否則就是 100%,比如設(shè)置了top:0;bottom:0;效果和設(shè)置height:100%;是一樣的。如果想要避免,就必須要設(shè)置height。
9.圖片預(yù)加載
這里說的預(yù)加載,不是懶加載。首先根據(jù)我個(gè)人理解科普下,懶加載和預(yù)加載的區(qū)別。
- 懶加載:頁面加載的時(shí)候,先加載一部分內(nèi)容(一般是先加載首屏內(nèi)容),其它內(nèi)容等到需要加載的時(shí)候再進(jìn)行加載。
- 預(yù)加載:頁面加載的時(shí)候,先加載一部分內(nèi)容(一般是先加載首屏內(nèi)容),其它內(nèi)容等到先加載的一部分內(nèi)容(一般是首屏內(nèi)容)加載完了,再進(jìn)行加載。
兩種方式,都是為了減少用戶進(jìn)入網(wǎng)站的時(shí)候,更快的看到首屏的內(nèi)容。
下面栗子,將這#preloader這個(gè)元素加入到到 HTML 中,就可以實(shí)現(xiàn)通過 CSS 的background屬性將圖片預(yù)加載到屏幕外的背景上。只要這些圖片的路徑保持不變,當(dāng)它們?cè)?Web 頁面的其他地方被調(diào)用時(shí),瀏覽器就會(huì)在渲染過程中使用預(yù)加載(緩存)的圖片。簡(jiǎn)單、高效,不需要任何 JavaScript。
#preloader {
/*需要預(yù)加載的圖片*/
background: url(image1.jpg) no-repeat,url(image2.jpg) no-repeat,url(image3.jpg) no-repeat;
width: 0px;
height: 0px;
display: inline;
}
但是這樣會(huì)有一個(gè)問題,因?yàn)?code>#preloader預(yù)加載的圖片,會(huì)和頁面上的其他內(nèi)容一起加載,增加了頁面的整體加載時(shí)間。所以需要用 JS 控制。
function preloader(urlArr,obj) {
var bgText='';
for(var i=0,len=urlArr.length;i<len;i++){
bgText+='url('+urlArr[i]+') no-repeat,';
}
obj.style.background=bgText.substr(0,bgText.length-1);
}
window.onload = function() {
preloader(['image1.jpg','image2.jpg','image3.jpg'],document.getElementById('preloader'));
}
原理也很簡(jiǎn)單,就是先讓首屏的圖片加載完,然后再加載其它的圖片。通過給#preloader設(shè)置背景圖片,加載所需要的圖片,然后頁面上需要加載這些圖片的時(shí)候,就直接從緩存里面拿圖片,不需要通過 HTTP 請(qǐng)求獲取圖片,這樣加載就很快。
10.慎用 * 通配符
在做網(wǎng)頁的時(shí)候,經(jīng)常會(huì)使用下面兩種方式重置樣式,以此來消除標(biāo)簽的默認(rèn)布局和不同瀏覽器對(duì)于同一個(gè)標(biāo)簽的渲染。
*{margin:0;padding:0;}
上面這種方式,代碼少,但是性能差,因?yàn)殇秩镜臅r(shí)候,要匹配頁面上所有的元素。很多基礎(chǔ)樣式?jīng)]有margin和padding的元素,比如div,li等都被匹配,完全沒必要。下面看另一種方式:
body,dl,dd,h1,h2,h3,h4,h5,h6,p,form,ol,ul{margin:0;padding:0;}
這種方式,代碼稍微多,但是性能比上面的方式好。在渲染的時(shí)候,只匹配body,dl,dd,h1,h2,h3,h4,h5,h6,p,form,ol,ul這里面的元素,這些元素帶有margin和padding,需要重置。
再看例子:
.test * {color: red;}
匹配文檔中所有的元素,然后分別向上逐級(jí)匹配class為test的元素,直到文檔的根節(jié)點(diǎn):
.test a {color: red;}
匹配文檔中所有a的元素,然后分別向上逐級(jí)匹配class為test的元素,直到文檔的根節(jié)點(diǎn)。
兩種方式,哪種更好不言而喻,所以在開發(fā)的時(shí)候,建議避免使用通配選擇器。
11.合并、壓縮 CSS
這個(gè)沒什么好解釋的,就是壓縮和合并 CSS。
首先壓縮 CSS,除了使用工具,比如 Gulp、Webpack 等把代碼壓縮,把空格和換行都去掉。還有一個(gè)建議就是屬性簡(jiǎn)寫。
比如:
margin-top:0;
margin-right:10px;
margin-bottom:10px;
margin-left:10px;
background-image: url('test.jpg');
background-position: top center;
background-repeat: no-repeat;
border-width:1px;
border-style:solid;
border-color:#000;
color:#0099FF;
可以換成下面的:
margin:0 10px 10px 10px;
background: url('test.jpg') no-repeat top center;
border:1px solid #000;
color:#09F;
至于合并的時(shí)候,我按照自己的開發(fā)習(xí)慣給幾個(gè)建議:
- 合并公用的樣式。比如項(xiàng)目的頭部、底部、側(cè)邊欄這些,一般都是公用的,這些可以寫在一個(gè)公用樣式表上,比如
main.css; - 上面所說的
main.css是每一個(gè)頁面都需要引入,而樣式重置表reset.css也是每一個(gè)頁面都需要用到的,那么建議main.css和reset.css合并成一個(gè)文件,給頁面引入,減少請(qǐng)求; - 每個(gè)頁面對(duì)應(yīng)的樣式為獨(dú)立的文件。比如首頁對(duì)應(yīng)的是
index.css,產(chǎn)品列表頁對(duì)應(yīng)的樣式是product-list.css。那么index.css就只在首頁引入,其它頁面不引入,因?yàn)橐爰儗倮速M(fèi)請(qǐng)求資源,其他頁面對(duì)應(yīng)的樣式也是這個(gè)處理方式。index.css、product-list.css等其它頁面的樣式就保留單獨(dú)的文件,不作合并處理。
12.CSS 在 head 引入
瀏覽器在所有的 stylesheets 加載完成之后,才會(huì)開始渲染整個(gè)頁面。在此之前,瀏覽器不會(huì)渲染頁面里的任何內(nèi)容,頁面會(huì)一直呈現(xiàn)空白。這也是為什么要把 stylesheet 放在頭部的原因。如果放在 HTML 頁面底部,頁面渲染就不僅僅是在等待 stylesheet 的加載,還要等待 HTML 內(nèi)容加載完成,這樣一來,用戶看到頁面的時(shí)間會(huì)更晚。
13.避免使用 @import
CSS 樣式文件有兩種引入方式,一種是link元素,另一種是@import。在這里,我建議就是避免使用@import。因?yàn)?code>@import會(huì)影響瀏覽器的并行下載,使得頁面在加載時(shí)增加額外的延遲,增添了額外的往返耗時(shí)。而且多個(gè)@import可能會(huì)導(dǎo)致下載順序紊亂。
比如一個(gè) CSS 文件index.css包含了以下內(nèi)容:@import url("reset.css")。那么瀏覽器就必須先把index.css下載、解析和執(zhí)行后,才下載、解析和執(zhí)行第二個(gè)文件reset.css。簡(jiǎn)單的解決方法是使用<link>替代@import。
14.從 PSD 文件思考怎么寫代碼
接到效果圖,先不用著急切圖,先看下 PSD 文件。思考下怎么排版,那些模塊可以做成公用的模塊,模塊應(yīng)該怎么命名,寫樣式等。當(dāng)我們拿到設(shè)計(jì)師給的 PSD 時(shí),首先不要急于寫 CSS 代碼,首先對(duì)整個(gè)頁面進(jìn)行分析,先思考下面幾點(diǎn):
- 分析頁面有哪些模塊是公用的。常見公用模塊有頭部、底部、菜單欄、懸浮按鈕等;
- 分析模塊有什么樣式,把公用的樣式提取出來,公用樣式包括公用的狀態(tài)樣式。比如按鈕、輸入框、下拉框等公用的選中狀態(tài),禁用狀態(tài)的樣式等。
15.小圖標(biāo)的處理方案
一個(gè)網(wǎng)站肯定會(huì)有很多個(gè)小圖標(biāo),對(duì)于這些小圖標(biāo),目前的解決方案有兩個(gè),CSS Sprite(雪碧圖),字體圖標(biāo),把圖片轉(zhuǎn)成 base64。下面對(duì)比一下這兩種方式:
-
CSS Sprite:把所有 icon 圖片合成一張 png 圖片,使用的是在對(duì)節(jié)點(diǎn)設(shè)置寬高,加上
bacgroud-position。以背景圖方式顯展示需要的 icon,如果一個(gè)網(wǎng)站有 20 圖標(biāo),那么就要請(qǐng)求 20 次。使用 CSS Sprite,只需要請(qǐng)求一次,大大的減少了 HTTP 請(qǐng)求。缺點(diǎn)就是管理不靈活,如果需要新增一個(gè)圖標(biāo),都需要改合并圖片的源文件,圖標(biāo)定位也要規(guī)范,不然容易干擾圖片之間的定位; - 字體圖標(biāo):簡(jiǎn)單粗暴的理解就是把所有的圖標(biāo)當(dāng)成一個(gè)字體處理,這樣不用去請(qǐng)求圖片。一般是使用 class 來定義圖標(biāo),要替換圖標(biāo)時(shí),只需更換樣式名,管理方便,語意明確,靈活放大縮小,并且不會(huì)造成失真,但是只支持單色的圖片。
- base64:另一種方案就是把小的 icon 圖片轉(zhuǎn)成 base64 編碼,這樣可以不用去請(qǐng)求圖片,把 base64 編碼直接整合到 JS 或者 CSS 里面,可以防止因?yàn)橐恍┫鄬?duì)路徑,或者圖片被不小刪除了等問題導(dǎo)致圖片 404 錯(cuò)誤。但是找個(gè)方式會(huì)生成一大串的 base64 編碼。一般來說,8K 以下的圖片才轉(zhuǎn)換成 base64 編碼。如果把一張 50K 的圖片轉(zhuǎn)成 base64 編碼,那么會(huì)生成超過 65000 個(gè)字符的 base64 編碼,字符的大小就已經(jīng)是將近 70K 了!建議就是:8K 以下的圖片才轉(zhuǎn)換成 base64 編碼。
16.不要在 ID 選擇器前面進(jìn)行嵌套或?qū)憳?biāo)簽
- ID 在頁面上本來就是唯一的,而且人家權(quán)值那么大,前方嵌套(
.content #test)完全是浪費(fèi)性能,以及多寫一些沒有意義的代碼。這個(gè)雖然是一句話,但是還是有人犯這樣的錯(cuò)。 - 除了嵌套,在 ID 的前面也不需要加標(biāo)簽或者其它選擇器。比如
div#test或者.test#test。這兩種方式完全是多余的,理由就是 ID 在頁面就是唯一的。前面加任何東西都是多余的!
17.把常用樣式抽封裝成公用樣式
把長(zhǎng)段相同樣式提取出來作為公用樣式使用,比如常用的清除浮動(dòng),單行超出顯示省略號(hào),多行超出省略號(hào)等。
如下栗子:
/*超出省略號(hào)*/
/*<p class='text-ellipsis'></p>*/
.text-ellipsis{
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
/*清除浮動(dòng)*/
/*<div class='clearfix'></div>*/
.clearfix:after {
display: block;
content: '';
clear: both;
height:0;
}
18.CSS3 動(dòng)畫的優(yōu)化
在我之前一篇文章(移動(dòng) Web 開發(fā)問題和優(yōu)化小結(jié)),也有寫過關(guān)于這個(gè)的優(yōu)化建議,之前說的兩個(gè)建議是:
- CSS3 動(dòng)畫或者過渡盡量使用
transform和opacity來實(shí)現(xiàn)動(dòng)畫,不要使用left和top; - 動(dòng)畫和過渡能用
CSS3解決的,就不要使用JS。如果是復(fù)雜的動(dòng)畫可以使用CSS3+JS(或者HTML5+CSS3+JS)配合開發(fā),效果只有想不到,沒有做不到。
下面補(bǔ)充一個(gè):動(dòng)畫不宜過多,尤其是手機(jī)網(wǎng)站,否則會(huì)出現(xiàn)性能的問題.比如 CPU 一下子就被占用滿了,掉幀等。而且,不建議給每一個(gè)元素都使用硬件加速。
參考鏈接:
19.Body 設(shè)置最小寬度
這個(gè)是在 PC 站會(huì)出現(xiàn)的問題,應(yīng)該大家都知道。下面簡(jiǎn)單說一下!
比如下面的栗子,一個(gè)網(wǎng)站,頁面內(nèi)容寬度是 1200px??粗苷#瑳]什么特別:

如果這個(gè)時(shí)候,把頁面窗口縮小。小于 1200px,頁面出現(xiàn)滾動(dòng)條,然后把滾動(dòng)條拖到最右邊:

這樣是不是就發(fā)現(xiàn),頂部的圖片和背景有一部分是斷層了!解決這個(gè)問題也很簡(jiǎn)單,就是給body加上min-width。值就是頁面寬度的值:body{min-width:1200px;}
重復(fù)上一步操作,無論怎么改變?yōu)g覽器窗口大小,都是正常的:

之所以會(huì)出現(xiàn)這樣的問題,是因?yàn)?,比如窗口縮小到 900px 的時(shí)候,小于內(nèi)容寬度的 1200px。就是出現(xiàn)橫向的滾動(dòng)條,但是body的寬度是 900px。這個(gè)時(shí)候,如果有元素(比如圖片的灰色區(qū)域和粉紅色的圖片)是相對(duì)body的width設(shè)置 100%,那么實(shí)際上這些元素的寬度也就是 900px。所以會(huì)出現(xiàn)斷層那些的視覺!解決方式就是給body加上min-width。讓body的寬度最小不會(huì)小于內(nèi)容的寬度!
20.小結(jié)
關(guān)于我對(duì) CSS 寫作建議和性能優(yōu)化的一個(gè)總結(jié),就到這里了。CSS 絕對(duì)不是那種只要能用就行,或者只要能用 CSS 把布局弄好就行的一門語言。CSS 給我的感覺,就是上手很簡(jiǎn)單,但是如果想用好 CSS,還是得花時(shí)間去研究。CSS 或者 CSS3,能夠優(yōu)化的東西還有很多,用好 CSS 或者 CSS3 能夠少寫很多 JS 代碼,做出來的東西也是很神奇,大家還是得繼續(xù)學(xué)習(xí)當(dāng)中的知識(shí)。
如果大家覺得我文章有哪個(gè)地方寫得不好,寫錯(cuò)了,歡迎指正。如果有什么其它的建議,歡迎指點(diǎn),讓大家互相交流,互相學(xué)習(xí),一起進(jìn)步!最后,祝大家節(jié)日快樂!