手把手帶你打造自己的UI樣式庫(第一章)

這一章裏主要是介紹一下頁面結構基礎,CSS 選擇器,頁面渲染機制,屏幕適配方案,CSS書寫規範。

頁面結構基礎

盒模型

在瀏覽器中,每一個 DOM 節點渲染後,都會在屏幕上佔用一個方形的區域,這個方形的區域就被稱爲盒子,我們把這種渲染方式叫盒模型。在盒模型中,我們主要介紹盒模型的主要屬性、兩種盒模型和邊距摺疊這三個內容:

一、盒模型主要屬性

在這裏插入圖片描述
上面的三幅畫,其實就是 HTML 中的三個盒子。對盒子大小和位置能產生影響的一共有四類屬性,他們分別是:

  • 寬度(width)和高度(height),這兩個屬性分別決定了一個盒子的寬度和高度,在標準盒模型中,這個寬高指定的就是最裏層內容區的寬度和高度,對應畫框中最裏面帶色彩的部分。
  • 內邊距(padding),當內容區和邊框需要離開一定的間距,避免佈局太過擁擠時,就可以用內邊距來指定他們隔開的距離。在畫框中就對應着內容區和邊框中間夾着的那部分白色的區域。
  • 邊框(border),邊框比較好理解,就是每幅畫的外框。
  • 外邊距(margin),外邊距是用來限制盒子與盒子中間的距離的,在上圖中,三幅畫邊框與邊框中間空出來的距離,就由外邊距來指定。
    把上面的畫框抽象一下,每個盒子的結構就是下圖所示:
    在這裏插入圖片描述

二、兩種盒模型

在盒模型的屬性除了標準盒模型外,還有在IE瀏覽器中使用的怪異盒模型。使用了 box-sizing:content-box;屬性的就是標準模式,用了 box-sizing:border-box;屬性的就是怪異模式。

如果我們給盒子都設置成 200px 的寬度,100px 的高度,10px 的 padding 和 1px 的 border。

  • 標準盒模型,標準盒模型這個標準是由 W3C 組織制定的,現在除了低版本IE以外,基本所有瀏覽器都遵循這個標準。標準盒模型中,width 和 height 屬性所指定的寬高就是實際內容區的大小,而盒子實際大小是:

橫向空間:width + padding寬度 + border寬度
縱向空間:height + padding寬度 + border寬度

可以看出來這個盒子的總寬度是 222px,也就是 200px 的 width,加上兩邊各 10px 的 padding,再加上兩邊各 1px 的邊框,實際佔用屏幕的寬度就是

200px + 10px2 + 1px2 = 222px。

在這裏插入圖片描述

  • 怪異盒模型,怪異盒模型是微軟在 IE6、7、8 中使用的一種盒模型,所以也把怪異盒模型叫做 IE盒模型。在怪異模式下,width 和 height 做指定的寬高就是盒子的實際寬高,而它內容區部分的大小是在 width 或 height 指定尺寸的基礎上,再減去 border 和 padding 所佔的寬度。

在怪異盒模型中,我們設置的寬度也是 200px,它的盒子佔用的橫向空間就是 200px,但是內容區的寬度變成了 178px,這就是用它的寬度減去了兩邊 padding 和兩邊 border 的結果。
在這裏插入圖片描述
標準盒模型是有缺陷的,在標準模式下,我們設置盒子的寬高並不是它實際所佔空間,所以在佈局的時候,就要再把盒子的 border 和 padding 計算一下來給這個盒子分配空間。但是在用 px 這種絕對單位指定盒子寬高的時候還能算一下應該把盒子設置成多大,但如果是用百分比指定盒子寬高的時候,就沒辦法算了。另外 border 只支持絕對寬度,而 px 和百分比混在一起做運算是比較困難的。比如一行四個盒子,每個盒子有 1px 的邊框,這時候按着標準盒模型給每個盒子25%的寬度,這四個盒子就會折行,不能放在一行。

這個時候就體現出怪異模型的好處了,假如一行四個盒子平均分配,在怪異模式中只需要每個盒子寬度 25%,然後 border 和 padding 隨便設置,瀏覽器會自己去算內容區應該有多大的。W3C 應該也是意識到了這個問題,但標準模式用了這麼多年,肯定是回不去了,也不能自己打臉說這些年我錯了。所以在 CSS3 中做了補救,就是加上了上面使用過的 box-sizing 屬性。

三、外邊距摺疊

最後說一個特殊的概念,就是外邊距摺疊。我們在前面討論的都是內容區、padding 和 border 間的關係,在這我們來說一下 margin。margin 的意思就是距別的框之間的距離,這很好理解。但是如果兩個盒子都設置了 margin,在排列這兩個盒子的時候並不會把兩個 margin 值的和作爲兩個盒子的間距。比如上下排列的盒子A和盒子B,盒子A的下邊距是10px,盒子 B 的上邊距是 20px,那麼在渲染時候,它倆之間的距離將會是 20px。就像A說我 10 米之內寸草不生;B說我20米內片甲不留。那把這倆盒子中間隔上個20 米,它倆就都是安全的了,而用不着隔開 30米。盒子間距的實際值其實就是取兩個盒子間margin值較大的那個。

文檔流

說完盒子模型,就可以說文檔流了。所謂文檔流,就是DOM節點排版佈局過程中,元素會自動從左往右,從上往下的流式排列。在一行裏,節點是按着從左到右排列,當這一行內容排滿的時候,就再單起一行排餘下的內容,再排滿就再新開一行。這裏面有幾個地方要注意:

  • 在正常佈局下,塊級元素會自己佔用一行,有幾個塊級元素就會有多少行。而行內元素纔會從左到右的排列。
  • 如果某元素用到了絕對定位或固定定位(absolute和fixed),那這個元素就會脫離文檔流,它前後的元素會忽略它的位置。而這個脫離文檔流的元素會按着指定的位置重新定位,如果沒指定位置,則會按着脫離文檔流之前的位置進行定位。
  • 如果某元素使用了浮動屬性(float),那這個元素也會脫離文檔流,浮動元素後面如果是塊級元素的話,就會忽略它的存在。但它和絕對定位不同的是,浮動元素雖然也會脫離文檔流,但會在文本的排布中佔有位置,浮動元素後面的文本會環繞着它進行排布。從下面的圖可以看出來,兩個紅色的塊是標準的文檔流,已經沒黃色的浮動的盒子什麼事了,但是第二個紅色塊中的文本卻受到了黃色塊的影響。
    在這裏插入圖片描述

頁面層級

剛說了文檔流在排列的時候,有元素脫離文檔流的情況,這時候不同的元素就會出現層疊的情況。或者在標準文檔流中,通過設置負值的 margin,也可能會導致元素互相遮蓋。從這種遮蓋關係來看,就知道 HTML 其實是一個三維的空間,有平面的 x,y 位置,也有 z 軸上的層疊關係。

在默認的情況下,即不指定層級的時候,所有元素是按下面的層級排列的:

  • HTML 在渲染的時候最先渲染的是標準文檔流,所以標準文檔流中的內容會被排在最下一層,標準文檔流中如果還有層級,那就是後出現的會擋住先出現的。
  • float 元素在標準文檔流之後渲染,所以 float 會在標準文檔流的上一層。
  • 絕對定位的元素最後渲染,所以默認情況下絕對定位元素會排在最上層。

如果對默認的層疊關係不滿意,我們就可以用 z-index 數值手動指定各個元素的層級關係。但這裏要注意,z-index 只對已經有定位的元素生效(position:relative/absolute/fixed),所以當寫樣式的時候遇到 z-index 沒有達到你預期效果的時候,考慮一下這種情況。

在使用 z-index 的時候,我們最好對整個項目的層級關係有個提前的設計,不要隨便用。當我們的頁面需要分成多個層級的時候,可以對每個層級設置一個 z-index 使用的範圍。比如一個頁面主要分爲內容層、導航層和蒙版提示層這三層,那麼就可以限制內容層的 z-index 值從 100 到 200 之間;導航層要覆蓋住內容層,z-index 可以用 200 到 300 這個範圍的值;蒙版提示層要蓋住一切,z-index 可以用 300 到400 這個範圍的值。這樣每一層的層級關係就是明確的,不會互相影響。

CSS選擇器

ID 選擇器

ID 選擇器是用 “#” 號加 ID 名稱 xxx 來表示,用來選擇 HTML 中 id=“xxx” 的 DOM 元素。

Tips:
1、ID 選擇器只能對一個元素生效,同一個頁面裏不允許出現兩個 ID 相同的元素。
2、理論上 ID 選擇器是效率最高的選擇器。但是由於它只能選一個元素,特異性太高,在實際開發中也很少在 CSS 裏使用 ID 選擇器。
3、也正是因爲 ID 選擇器特異性高,所以在 JS 裏使用 ID 選擇器的比較常見。

類選擇器

類選擇器是用 “.” 加上 class 名稱來表示,用來選擇 HTML 中 class=“xxx” 的 DOM 元素。

Tips:
1、類選擇器的效率也是不錯的,和 ID 選擇器並不會有太大的差異。所以在寫 CSS 的時候,比較推薦用類選擇器。
2、類選擇器會選擇到所有類名相同的 DOM 元素,沒有數量限制。
3、類選擇器應該是樣式開發中應用最多的選擇器。

通配選擇器

通配選擇器使用星號來選擇到頁面裏所有元素。用法如下:

*{
    margin: 0;
    padding: 0;
}

上面這個樣式就是把所有元素的內外邊距都歸零。由於通配選擇器要把樣式覆蓋到所有的元素上,可想而知它的效率並不會高,所以在實際開發中一般不建議使用通配選擇器。

標籤選擇器

標籤選擇器的作用是選中 HTML 中某一種類的標籤,它直接使用 HTML 中的標籤名作爲選擇器的名稱。比如我們需要把頁面裏所有大標題的字號都調成 20px,就可以用標籤選擇器來實現:

h1{
    font-size: 20px;
}

Tips: 標籤選擇器通常用來重置某些標籤的樣式,標籤選擇器的效率也不是很高,但要好過通配選擇器。

屬性選擇器

屬性選擇器比較好理解,就是通過 DOM 的屬性來選擇該 DOM 節點。屬性選擇器是用中括號 “[]” 包裹.
屬性選擇器有如下幾種形式:

  • [attr],用來選擇帶有 attr 屬性的元素,如剛提到的 a [href]。
  • [attr=xxx],用來選擇有 attr 屬性且屬性值等於 xxx 的元素,如選擇所有文本類型的輸入框,可以用 input [type=text]。
  • [attr~=xxx],這個選擇器中間用了~=,選擇屬性值中包含 xxx 的元素,但一定是逗號分隔的多個值中有一個能和 xxx 相等才行。
  • [attr|=xxx],這個選擇器是用來選擇屬性值爲 xxx 或 xxx- 開頭的元素,比較常用的場景是選擇某一類的屬性。
  • [attr^=xxx],這個選擇器會匹配以 xxx 開頭的元素,實際上就是用正則去匹配屬性值,只要是以 xxx 開頭都可以。
  • [attr$=xxx],這個選擇器和上一個相似,它是用正則匹配的方式來選擇屬性值以 xxx 結尾的元素。
  • [attr*=xxx],最後一個,這個是用正則匹配的方式來選擇屬性值中包含 xxx 字符的所有元素。這個選擇器的規則算是最寬泛的,只要 xxx 是元素屬性值的子字符串,這個選擇器就會生效。

Tips:

  1. 屬性選擇器要做文本的匹配,所以效率也不會高。
  2. 在使用屬性選擇器時,儘量要給它設置上生效的範圍,如果只用了個 [href] 相當於要在所有元素裏找帶 href 的元素,效率會很低。如果用 a [href] 會好的多,如果用 .link [href] 就更好了。這種組合方式我們在下一節講解。
  3. 屬性選擇器很靈活,如果能使用 CSS 代替 JS 解決一些需求,可以不用太糾結性能的問題,用 JS 實現也一樣要耗費資源的。

後代選擇器

後代選擇器的語法是用空格分隔的多個選擇器組合,它的作用是在 A 選擇器的後代元素中找到 B 選擇器所指的元素。它的語法形式就是:“選擇器 A 選擇器 B”

Tips:後代選擇器通常用來限制選擇器生效的範圍,防止因爲選擇器使用不當或者對元素命名出現重複造成的樣式衝突。

子元素選擇器

子元素選擇器和後代選擇器類似,也是爲選擇器限定範圍。不同的是子元素選擇器只找子元素,而不會把所有的後代都找一遍。它的語法是 “選擇器 A> 選擇器 B”

Tips:子元素選擇器的作用和後代選擇器相似,也是用來限制選擇器生效的範圍。它和後代選擇器不同的是:

  1. 子元素選擇器只匹配子元素,不會匹配後代元素。在有確定的父子關係時,儘量使用子元素選擇器,效率會比後代選擇器高。
  2. 使用子元素選擇器還可以避免對非直接後代的樣式影響,在只想給子元素設置樣式時會比後代選擇器安全。

兄弟選擇器

在 CSS 中,還有一種選擇器是用來選取同級元素的,叫做兄弟選擇器。兄弟選擇器有兩種,一種是相鄰兄弟選擇器,另外一種是通用兄弟選擇器。

一、相鄰兄弟選擇器

相鄰兄弟選擇器是用來選取某個元素緊鄰的兄弟元素,它的語法是 “選擇器 A + 選擇器 B”,表示找到與 A 元素相鄰的 B 元素。其實就是對選擇器 B 加上 “緊鄰着選擇器 A” 的限制。

Tips:相鄰兄弟選擇器通常有兩類用處:

  1. 用於自動調整佔位,比如後面在佈局的時候,有 header 和沒 header 情況下內容區的高度會不同,就可以使用相鄰兄弟選擇器來控制內容區的高度。
  2. 相鄰兄弟選擇器的第二種用法是用來控制相同元素中間的間隔,比如在 List 組件開發時,每個 li 元素之間要加上分割線的需求就會通過相鄰兄弟選擇器來實現。

二、通用兄弟選擇器

通用兄弟選擇器和相鄰兄弟選擇器很相似,它的語法是 “選擇器 A ~ 選擇器 B”,也是用選擇器 A 做限制,選擇器 B 是最終匹配的目標。不同的是通用兄弟選擇器會匹配選擇器 A 指定元素後面的所有符合選擇器 B 規則的元素。

Tips:
兄弟選擇器(包括相鄰兄弟選擇器和通用兄弟選擇器)中都是隻能向後選擇,如果需要向前選擇,就只能給前面的元素指定上 class,再用類選擇器來實現了。這裏爲什麼不供向前尋找的方式,我們留個懸念,後面在講渲染原理的時候再來分析。

交集選擇器

交集選擇器是爲了找兩個或多個選擇器的交集,用法就是把兩個選擇器放在一起,形式如 “選擇器 A 選擇器 B”,中間不需要加空格或者其他符號。交集選擇器最主要的作用是在限定範圍內標識特殊的樣式。

並集選擇器

並集選擇器是爲了合併類似的樣式,可以把選擇器不同但樣式相同的 CSS 語法塊做合併。並集選擇器就是用逗號分割多個選擇器,形式如 “選擇器 A, 選擇器 B”,表示該樣式對選擇器 A 和選擇器 B 所選擇的元素都生效。

僞類選擇器

在頁面中,有時候同一個元素在不同動作下有不同的樣式。比如鏈接在沒有點擊的時候有個樣式,在鼠標放上去有另外的樣式,還有在點擊完成以後又會又一個樣式。這幾種情況下這個鏈接的標籤並沒有變化,有變化的只是它的狀態,這時候就可以裏用僞類來實現這個需求。在瀏覽器中,僞類的出現是爲了向某些選擇器添加特殊的效果或限制。僞類是在正常的選擇器後面加上僞類名稱,中間用冒號(:)隔開。

僞類主要有兩方面的用處,一方面是標記一些特殊的狀態;另外還有一類僞類是有篩選的功能。

一、標記狀態的僞類:

可以標記元素狀態的僞類有如下幾種:

  • :link,選取未訪問過的超鏈接元素。如果我們注意過搜索引擎的結果的話,它裏面的鏈接只要點過的就會變色,從而標記哪個鏈接是訪問過的。:link 這個屬性就是用來標識沒訪問過的鏈接。
  • :visited,選取訪問過的超鏈接元素。和第一條相反,:visited 是用來標記哪個鏈接是已經訪問過的,防止重複點擊。
  • :hover,選取鼠標懸停的元素。,這個僞類經常用在 PC 端,當鼠標放在一個元素上時,可以用 :hover 來控制鼠標懸停的樣式。因爲在移動端裏沒有鼠標的概念,所以移動端裏很少用這個僞類。
  • :active,選取點中的元素。這個僞類的作用在剛纔提到過了,當我們希望按鈕有操作反饋的時候,可以用它來標記操作反饋的樣式。當然這個僞類也是可以通用的,並不是只能用在按鈕上。
  • :focus,選取獲得焦點的元素。這個僞類用來標識獲得焦點的元素,比如搜索框在聚焦的時候有個比較明顯的邊框,方便用戶知道當前在可輸入的狀態。

二、篩選功能的僞類:

有些僞類也可以有篩選的功能,可以根據元素的特點或者索引來給特定的元素加上樣式。常用的有篩選功能的僞類如下:

  • :empty,選取沒有子元素的元素。比如選擇空的 span,就可以用 span:empty 選擇器來選擇。這裏要注意元素內有空格的話也不能算空,不會被這個僞類選中。
  • :checked,選取勾選狀態的 input 元素, 只對 radio 和 checkbox 生效。
  • :disabled,選取禁用的表單元素。
  • :first-child,選取當前選擇器下第一個元素。
  • :last-child,和 first-child 相反,選取當前選擇器下最後一個元素。
  • :nth-child(an+b),選取指定位置的元素。這個僞類是有參數的,參數可以支持 an+b 的形式,這裏 a 和 b 都是可變的,n 從0起。使用這個僞類可以做到選擇第幾個,或者選擇序號符合 an+b 的所有元素。比如使用 li:nth-child(2n+1),就可以選中 li 元素中序號是2的整數倍加1的所有元素,也就是第1、3、5、7、9、2n+1個 li 元素。
  • :nth-last-child(an+b) ,這個僞類和 nth-child 相似,只不過在計數的時候,這個僞類是從後往前計數。
  • :only-child,選取唯一子元素。如果一個元素的父元素只有它一個子元素,這個僞類就會生效。如果一個元素還有兄弟元素,這個僞類就不會對它生效。
  • :only-of-type,選取唯一的某個類型的元素。如果一個元素的父元素裏只有它一個當前類型的元素,這個僞類就會生效。這個僞類允許父元素裏有其他元素,只要不和自己一樣就可以。

這個項目中會用到具有篩選功能的“:nth-child(an+b)”選擇器,我們通過使用這個僞類按着網格排布的規律選擇出需要加邊框的元素。

僞元素選擇器

僞元素選擇器是用於向某些元素設置特殊效果。僞元素選擇器選中的並不是真實的 DOM 元素,所以叫僞元素選擇器。僞元素選擇器常用的也就下面 5 個:

  • ::first-line,爲某個元素的第一行文字使用樣式。
  • ::first-letter,爲某個元素中的文字的首字母或第一個字使用樣式。
  • ::before,在某個元素之前插入一些內容。
  • ::after,在某個元素之後插入一些內容。
  • ::selection,對光標選中的元素添加樣式。

Tips:

  • 僞元素選擇器構造的元素是虛擬的,所以不能用 JS 去操作它。
  • 如果同時使用了 before 和 first-letter 兩個僞類,第一個字是要從 before 裏的內容開始算起的,如果 before裏面的內容是一個非文本元素,那 first-letter 也會作用在這個非文本元素上,但是不一定能生效。
  • first-line 和 first-letter 不適用於內聯元素,在內聯元素中這兩個選擇器都會失效。
  • 在 CSS3 中,規定了僞類用一個冒號(:)表示,僞元素用兩個冒號表示(::)。但除了 selection,其餘四個僞元素選擇器已經在 CSS2 中存在且和僞類用的是一樣的單冒號表示的。爲了向下兼容,現在的瀏覽器中僞元素選擇器用單冒號和雙冒號都可以。在沒有兼容問題的情況下,還是建議大家按着新的 CSS3 標準來開發。

CSS 選擇器相關的文檔在W3C網站:https://www.w3.org/TR/selectors/#selectors 上進行查看

頁面渲染機制

頁面的加載和渲染全過程

當我們在瀏覽器裏輸入一個 URL 後,最終會呈現一個完整的網頁。這中間會經歷如下的過程:

  • HTML 的加載
    輸入 URL 後,最先拿到的是 HTML 文件。HTML是一個網頁的基礎,所以要在最開始的時候下載它。HTML下載完成以後就會開始對它進行解析。
  • 其他靜態資源下載
    HTML 在解析的過程中,如果發現 HTML 文本里面夾雜的一些外部的資源鏈接,比如 CSS、JS 和圖片等時,會立即啓用別的線程下載這些靜態資源。這裏有個特殊的是 JS 文件,當遇到 JS 文件的時候,HTML 的解析會停下來,等 JS 文件下載結束並且執行完,HTML 的解析工作再接着來。這樣做是因爲 JS 裏可能會出現修改已經完成的解析結果,有白白浪費資源的風險,所以 HTML 解析器乾脆等 JS 折騰完了再幹。
  • DOM 樹構建
    在 HTML 解析的同時,解析器會把解析完的HTML轉化成DOM 對象,再進一步構建 DOM 樹。
  • CSSOM 樹構建
    當 CSS 下載完,CSS 解析器就開始對 CSS 進行解析,把 CSS 解析成 CSS 對象,然後把這些 CSS 對象組裝起來,構建出一棵 CSSOM 樹。
  • 渲染樹構建
    DOM 樹和 CSSOM 樹都構建完成以後,瀏覽器會根據這兩棵樹構建出一棵渲染樹。
  • 佈局計算
    渲染樹構建完成以後,所有元素的位置關係和需要應用的樣式就確定了。這時候瀏覽器會計算出所有元素的大小和絕對位置。
  • 渲染
    佈局計算完成以後,瀏覽器就可以在頁面上渲染元素了。比如從 (x1, y1) 到(x2, y2)的正方形區域渲染成藍色。經過渲染引擎的處理後,整個頁面就顯示在了屏幕上。

DOM 樹的構建

頁面中的每一個 HTML 標籤,都會被瀏覽器解析成一個對象,我們稱它爲文檔對象(Document Object)。HTML 的本質是一個嵌套結構,在解析的時候會把每個文檔對象用一個樹形結構組織起來,所有的文檔對象都會掛在一個叫做 Document 的東西上,這種組織方式就是 HTML 最基礎的結構–文檔對象模型(DOM),這棵樹裏面的每個文檔對象就叫做 DOM 節點。

在 HTML 加載的過程中,DOM 樹就在開始構建了。構建的過程是先把 HTML 裏每個標籤都解析成 DOM 節點(每個標籤的屬性、值和上下文關係等都在這個文檔對象裏),然後使用深度遍歷的方法把這些對象構造成一棵樹。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" href="./index.css">
</head>
<body>
    <div class="header">
        <span class="page-name">文章詳情頁</span>
    </div>
    <div class="content">
        <h1 class="title">文章標題</h1>
        <div class="article">
            <p class="graph">吃葡萄不吐葡萄皮</p>
            <img src="./test.jpg" alt="文章插圖">
            <p class="graph">不吃葡萄倒吐葡萄皮</p>
        </div>
    </div>
</body>
</html>

在構建 DOM 樹的時候,就是從最外層 HTML 節點開始,按深度優先的方式構建。之所以用深度優先,是因爲 HTML在加載的時候是自上而下的,最先加載的是根節點,然後是根節點的第一個子節點,再然後是head的第一個子節點…head構建完成後再去構建 body 部分的內容,以此類推。使用深度優先的方式構建這棵樹就和文檔的加載順序吻合了。
在這裏插入圖片描述

CSSOM 樹的構建

在瀏覽器構建 DOM 樹的同時,如果樣式也加載完成了,那麼 CSSOM 樹也在同步地構建。CSS 樹和 DOM 類似,它的樹形結構記錄着所有樣式的信息。

body{
    font-size: 16px;
}
// 去掉所有p元素的內外邊距
p{
    margin: 0;
    padding: 0;
}
// 頁面頭部行高50px,文本垂直居中,隱藏
.header{
    height: 50px;
    line-height: 50px; 
    display: none;
    text-align: center;
}
.header .page-name{
    font-size: 20px;
}
// 文本區域左右兩邊留10px空白
.content{
    padding: 0 10px; 
}
.contetn .title{
    font-seize: 20px;
}
// 內容區行高30px
.content .graph{
    line-height: 30px; 
}
// 文章中的圖片用作塊級元素,水平居中
.content img{
    display: block;
    margin: 0 auto;
}

我們就以這一組樣式爲例,這樣一組樣式中有公用的樣式 p 和 body,有標題欄 .header 部分的樣式,還有內容區 .content 部分的樣式。這樣通過解析器的構造,可以得到類似下面這樣的一棵 CSSOM 樹:
在這裏插入圖片描述

Tips:
1、 這棵樹是一個示意圖,並不是瀏覽器裏構造 CSSOM 樹的真實的數據結構,而且各種瀏覽器內核實現 CSSOM 樹的方式也不全都相同。這部分內容可以參考 Google Web Fundamentals ,它把 CSSOM 樹描述成自上而下建立的結構,類似這樣:
在這裏插入圖片描述
這裏也是按着這個文檔裏 CSSOM 的模型來示意的,不同的是把 HTML 節點作爲了根節點。這是因爲考慮到給 HTML 標籤設置樣式的時候同樣會生效,所以 HTML 標籤應該也存在於 CSSOM 樹中。
2、CSSOM 樹和 DOM 樹是獨立的兩個數據結構,它們沒有一一對應關係。DOM 樹描述的是 HTML 標籤的層級關係,CSSOM 樹描述的是選擇器之間的層級關係。
3、在 CSS 中存在樣式的繼承機制,有些屬性在父節點設置後,在其後代節點都會具備這個樣式。比如我們在 HTML 上設置一個 “font-size:20px;”,那麼頁面裏基本所有的標籤都可以繼承到這個屬性了。當然不是所有標籤和屬性都可以有繼承特性的,比如 border 這種屬性就不是可繼承的。如果 border 可繼承了,那麼在一個父元素裏設置上以後,所有子元素都會有個邊框,這顯然是不合理的。所以在大部分情況下,通過這種推理,就能知道哪些樣式是可以繼承的,而哪些不行。

渲染樹(Render Tree)的構建

在 DOM 樹和 CSSOM 樹都渲染完成以後,就會進入渲染樹的構建工作。渲染樹就是對 DOM 樹和 CSSOM 樹的結合,得到一個可以知道每個節點會應用什麼樣式的數據結構。這個結合的過程大體上是遍歷整個 DOM 樹,然後在 CSSOM 樹裏查詢到匹配的樣式。但在不同瀏覽器裏這個過程也不太一樣,在 Chrome 裏會在每個節點上使用 attach() 方法,把 CSSOM 樹的節點掛在 DOM 樹上作爲渲染樹。然而在 Firefox 裏,會單獨構造一個新的結構,用來連接 DOM 樹和 CSSOM 樹的映射關係。它們內部的實現方式有所不同,但它們構造出來的渲染樹是有很多共同點的。渲染樹會有以下的特點:

1. 渲染樹的根是 HTML 節點。

在 Google Web Fundamentals 這個文檔中,渲染樹的根節點是 body,但實際上 HTML 節點上的樣式也是可以顯示在頁面上的,所以渲染樹也應該是由 HTML 節點開始,但是 head 標籤裏的內容和顯示沒有關係,所以渲染樹中可以沒有 head 標籤的部分。

2. 渲染樹和 DOM 樹 的結構並不完全一致。

渲染樹裏會把所有不可見的元素忽略掉,所以如果是 DOM 樹中的節點有 “display: none;” 屬性的節點以及它的子節點,最終都不會出現在渲染樹中。但是具有 “visibility: hidden;” 樣式的元素會出現在渲染樹中,因爲具有這個樣式的元素是需要佔位的,只不過不需要顯示出來。

3. 樣式優先級關係。

同一個 DOM 節點可能會匹配到多個 CSSOM 節點,而最終的表現由哪個 CSS 規則來確定,就是樣式優先級的問題了。當一個 DOM 元素受到多條樣式控制的時候,樣式的優先級順序應該是

內聯樣式 > ID選擇器 > 類選擇器 > 標籤選擇器 > 通用選擇器 > 繼承樣式 > 瀏覽器默認樣式

在有相同類型選擇器的時候,還有一套計算方法,給不同的選擇器都賦了一個權重值。當考察優先級的時候,直接用公式計算整條選擇器的權重作爲該樣式的優先級。比如:

  • 內聯樣式的權重是 1000。
  • ID 選擇器裏樣式的權重是 100。
  • 類選擇器、屬性選擇器和僞類選擇器裏樣式的權重是 10。
  • 標籤選擇器裏樣式的權重是 1。
  • 通用選擇器直接忽略。

那麼在計算的時候,假設一個選擇器裏有 a 個權重值是 100的、b 個權重值是 10 的和 c 個權重值是1的選擇器。那麼這個選擇器的權重值就 a100 + b10 + c。

Tips:
這個計算公式的形式就是這樣,但有幾點要注意:
1、這個計算模型僅供理解樣式優先級關係,不能代表瀏覽器裏真實的計算方法。
2、權重值的計算不能越級,比如選擇器 A 只有 1 個 ID 選擇器,權重就是 100;選擇器 B 用了 20 個類選擇器,權重值是 200。這個時候如果兩個選擇器對應的樣式作用在同一個 DOM 節點上,那麼還是選擇器A會生效,因爲它的選擇器級別更高。
3、如果兩個選擇器 A 和 B 是同級別選擇器,並且最終計算的權重值也相同,那麼這兩個選擇器誰在後面誰優先級高。

參考上面渲染樹的特點,由之前的 DOM 樹和 CSSOM 樹就可以構建出來一棵如下圖的渲染樹:
在這裏插入圖片描述
這棵渲染樹中的節點是和 DOM 樹中的節點對應的,而黃色框部分的內容就是從 CSSOM 樹中查找出來的。從上圖裏可以看出來,這棵渲染樹中去掉了 head 標籤裏的內容,也去掉了有 “display:none;” 樣式的 .header 元素及其子元素。而渲染樹裏面的 p.graph 元素會同時對應兩條樣式。這就是一棵由 DOM 樹和 CSSOM 樹結合而來的渲染樹。

Tips:
1、渲染樹的構建過程中,會遍歷 DOM 樹中的可見節點,然後在 CSSOM 樹中查找每個節點匹配的樣式,最後通過組合這些可見節點以及和它們相匹配的樣式就可以構建出一棵渲染樹(帶有“visibility: hidden;”屬性的元素不可見,但會在頁面中佔位,所以會出現在渲染樹中)。這裏在查找的時候,出於效率的考慮,會從 CSSOM 樹的葉子節點開始查找,對應在 CSS 選擇器上也就是從選擇器的最右側向左查找。這就是在 2-4 講解後代選擇器時提到使用“.page .article p”會有效率問題的原因,這個選擇器中會最先在 CSSOM 的所有葉子節點裏查找 p 標籤,這種標籤類的選擇器會很多且沒有索引,會造成查找效率低下。不建議使用標籤選擇器和通配選擇器的原因也是這個。
2、在 2-4 講解兄弟選擇器的時候,說過兄弟選擇器爲什麼只能向後尋找兄弟元素。這是因爲在生成渲染樹的時候會遍歷 DOM 節點來生成渲染樹的節點,當遇到兄弟選擇器的時候,它前面的兄弟元素在渲染樹上的節點已經生成完畢,而它後面的兄弟節點還沒有生成。這時候如果再回頭去改前面兄弟節點的那就麻煩了,整個遍歷的規則都要變化,而後面兄弟節點在生成的時候把兄弟選擇器的影響加進去就可以。所以這就是爲什麼兄弟選擇器只能向後尋找兄弟元素,而沒提供向前尋找的方式。

佈局(Layout)

經過上面的步驟,生成了一棵渲染樹,這棵樹就是我們展示頁面的關鍵。通過計算渲染樹上每個節點的樣式,就能得出來每個元素所佔空間的大小和位置。當有了所有元素的大小和位置後,就可以在瀏覽器的頁面區域裏去繪製元素的邊框了。這個過程就是佈局,英文中會用 Layout 這個詞來描述。

繪製(Paint)

經過佈局,每個元素的位置和大小就有了,經過最後繪製這一步,就可以把樣式可視化的展現在屏幕上了。在繪製的過程中,瀏覽器會調用圖形處理器,逐層逐塊的把所有計算好位置和樣式的元素都繪製出來。

當繪製工作結束,我們的頁面就終於展示在瀏覽器上了。

重排(Reflow)與重繪(Repaint)

渲染樹是動態構建的,DOM 節點和 CSS 節點的改動都可能會造成渲染樹的重建。渲染樹的改動就會造成重排或者重繪.

1、重排。

當我們在 DOM 樹中新增、刪除了元素,或者是改變了某些元素的大小、位置、佈局方式等,在這個時候渲染樹裏這個有改動的節點和它會影響的節點,都要重新計算。在改動發生時,要重新經歷 DOM 的改動、 CSSOM 樹的構建、渲染樹的構建、佈局和繪製整個流程,這個過程就叫做“重排”,也有的叫做“迴流”。

以剛纔代碼中隱藏的 .header 元素爲例,假如我們通過 JS 把它的 “display:none;” 屬性去掉,那麼它就要顯示在屏幕中。這種情況下會經歷下面的過程

  • DOM 樹沒有變化。
  • CSSOM 樹中這個樣式節點裏的 display 屬性沒有了。
  • 渲染樹中的變化就比較大了,因爲之前 “display:none;” 的元素沒有出現在渲染樹中,所以這個時候渲染樹就要再重新結合 DOM 樹和 CSSOM 樹,把 .header 這個元素和它的子元素都加到渲染樹中來。
  • 佈局的過程也會有不小的花銷,需要給新加進來的 .header 元素找到位置,然後再把後面影響到的所有元素的大小和位置都重新計算一遍。這樣得到一個新的佈局值。
  • 最後就是按着新的佈局,把 .header 和受它影響的元素都重新繪製一遍,這個頁面的改動就生效了。
2、重繪

重繪是當我們改變元素的字體顏色、背景色等外觀元素的時候,並不會改變它的大小和位置,也不會影響到其他元素的佈局,這個時候就沒有必要再重新構建渲染樹了。瀏覽器會直接對元素的樣式重新繪製,這個過程就叫做“重繪”。

我們還以上面的代碼爲例,假如我們想對 .content 元素加一個 “color: black;” 的樣式。這個時候就會經歷以下的過程:

  • DOM 樹沒有變化。
  • CSSOM 樹中 .content 對應的節點加入一條 “color: black;” 的樣式。
  • Color 屬性的改變不會造成渲染樹結構的變化,所以會在現有的渲染樹中找出 .content 元素,給它加上 “color: black;” 的樣式。
  • 因爲存在樣式繼承機制,所以瀏覽器還會找到 .content 元素的子元素,如果有可以繼承的節點,那麼也要給這些節點加上 “color: black;” 的樣式,這個例子中就會在 h1.title、p.graph 元素上都加入 “color: black;” 的樣式。
  • 不涉及位置變動,佈局過程直接忽略。
  • 對 .content 元素及其子元素佔用的塊重新繪製。

相對來說重排操作的消耗會比較大,所以在操作中儘量少的造成頁面的重排。

Tips:
爲了減少重排,可以通過幾種方式優化:
1、不要逐項的更改樣式,可以把需要改動的樣式收集到一塊,用一次操作改變。
2、可以使用 class 的變動代替樣式的改變,也能達到第1條的效果。
3、不要循環操作 DOM,循環的結果也要緩存起來,最後用一次操作來完成。
4、需要頻繁改動的元素(比如動畫)儘量使用絕對定位,脫離文檔流的元素會減少對後面元素的影響。
5、在條件允許的情況下儘量使用 CSS3 動畫,它可以調用 GPU 執行渲染。

CSS屏幕適配方案

爲什麼需要適配

現在市面上的設備尺寸沒有統一的標準,考慮到設備的用途不同,將來也不會統一。所以我們前端開發人員就要通過屏幕適配,保證我們開發的頁面在絕大多數設備上都可以正常顯示。這裏說的絕大部分而不是全部設備,是因爲確實存在個別偏差比較大的設備,如果這部分用戶量很少且適配代價會很大,那麼忽略這一小部分的設備也是可以的。適配的最終目的,就是爲了讓設計稿在大部分的移動設備上看起來有一致的展示效果。

計量單位

在講適配之前,我們要先介紹一下和設備有關的計量單位。知道這些計量單位以及它們之間的換算關係後,才能更好的理解後面要講的屏幕適配方案。

一、物理像素

現在的設備基本上用的都是液晶屏,這種屏幕的顯示原理就是在一塊屏幕區域上規則排布很多個發光二極管,系統通過控制這些發光點的顏色和亮度來達到顯示的作用。在一塊屏幕區域上,每一個發光點,就是我們說的一個物理像素。一些屏幕廠商標識的分辨率指的就是這個物理像素的多少,比如華爲 P30 手機標的屏幕像素是 1080×2340,就表示這塊屏幕上每行有 1080 個物理像素點,每列上有 2340 個物理像素點。

Tips:
1、物理像素點是屏幕的自身屬性,設備一出廠這個數值就是固定的了。
2、物理像素只是設備的屬性,一般和開發者沒什麼關係,我們的代碼也不會直接去操作物理像素。

二、邏輯像素 px

我們在寫樣式的時候,用的最多的應該就是 px 這個單位,這個 px 就是邏輯像素。現在的設備寬度最小的有 320 個物理像素,最大的能到一兩千,所以設備在顯示的時候如果直接使用物理像素來顯示,那顯示效果間的差異是非常大的。比如一個寬 320 像素的按鈕,在寬 320 物理像素的屏幕上會佔滿正個屏幕,而在寬 1242 物理像素的屏幕上,這個按鈕只能佔差不多四分之一的寬度,這個差距實在太大,並且不容易抹平。

所以,這些設備廠商爲了解決這個問題,提出了邏輯像素的概念。邏輯像素的出現就是要儘量抹平不同物理像素設備間的顯示差異。還以剛纔的例子來說,320 物理像素寬的設備上,我們讓 1 邏輯像素 = 1 物理像素,那整個寬度還是能容下 320px 的內容;而對於 1242 寬度的設備,我們讓它 1 邏輯像素 = 3 物理像素,這樣相當於這塊屏幕可以放下 414px 的內容。這時候再定義一個 320像素寬的按鈕,在低分辨率上是佔滿整個寬度,而在高清屏上也能佔四分之三左右的寬度,這種顯示效果就已經很相近了。

之所以屏幕的邏輯像素沒有統一成一種,一是因爲屏幕的物理尺寸不一樣,二是因爲大的屏幕就是要多顯示一點東西,不能完全遷就最低配置的屏幕。廠商可以通過控制邏輯像素和物理像素間的換算係數,來保證同樣 px 的元素在不同屏幕上顯示出來的大小盡量相似。

Tips:
高分辨率屏幕上會把多個物理像素當成 1 個邏輯像素來用,但不會和低清屏幕上一樣,把 1 個邏輯像素裏的多個物理像素顯示成同樣的顏色。所以高分辨率屏幕雖然和低分辨率屏幕的邏輯像素差不多,但在顯示細節和清晰度上還是有區別的,這就是爲什麼高清屏的顯示效果會更細膩。

三、設計像素

設計像素是指 UI 設計師做設計稿時使用的像素值,通常情況下是按着設備的物理像素來確定設計稿尺寸的。

因爲設備雖然邏輯像素差不多少,但同樣 1px 的內容,有的設備用 1 個物理像素點顯示,而有的用 9 個物理像素點來顯示。如果設計稿使用的分辨率夠大,那麼高分辨率的屏幕裏每 px 裏的 9 個物理像素可以顯示不同的顏色,而低分辨率屏幕裏只能把圖片裏相近的顏色做合併,顯示在一個物理像素上。所以用高清的設計稿,最終做出來的顯示效果會更細膩。在公司裏,設計像素一般用到 1080 的寬度也就差不多了。

四、相對像素 em 和 rem

我們剛纔說的 px 單位,它是一種絕對像素,給一個元素多少 px,那麼這個元素的寬就定死了。而在 CSS 裏還有一種計量方式,就是相對像素。相對像素在響應式開發的時候會顯得更靈活。相對像素的單位有 em 和 rem 兩種:

  • 相對像素 em。em 這個單位是相對於父元素的 font-size 字號值,實際就是父元素 font-size 的幾倍。這個字號可以是 CSS 指定的,也可以是從父元素的父元素繼承來的,如果前兩個都沒有,就會使用默認值(一般是16px)。假如父元素指定了 “font-size:20px;” ,子元素使用了 “width: 8em” ,那麼子元素的實際寬度就是 160px。
    我們在使用 em 單位的時候,如果 HTML 層級太深,這個相對關係就容易亂,並且其中一個節點改動字號的話,裏面的子元素的 em 值都要重新調整,會造成不便。
  • 相對像素 rem。rem 和 em 很相似,只不過它是相對於根節點,也就是 HTML 節點的 font-size 屬性指定的 px 值。頁面裏所有用 rem 作爲長度單位的元素,就都以 HTML 爲準,這樣就不用使用 em 那種比較麻煩的換算了,並且當頁面裏的元素改變字號時,不會對其他元素造成影響。

Tips:
em 是一個很早就出現的單位,IE6 都可以兼容。但是 rem 是 CSS3 的屬性,使用的時候要注意它的兼容性問題。目前 rem 通常應用在移動端的 WEB 開發上。

屏幕適配方案

在做屏幕適配的時候,通常是選擇一個大小適中的設備作爲基準尺寸,然後再對其他尺寸的設備做適配。比較常見的適配方法有下面幾種:

一、百分比方式適配

用百分比做適配的方式是比較原始的一種適配方式。我們通過給元素設置一個百分比的值,讓這個元素自動調整它的大小或位置等。這種方式出現的最早,也是在使用的時候最方便的。Bootstrap 框架在很多地方就採用了這種方案。

百分比適配有如下優點:

  • 簡單便捷,不用關心元素的絕對寬度。
  • 基本沒有兼容性問題。

但是這種適配方式缺點也是很明顯的:

  • 使用百分比時候,很容易不是整數,比如設計稿是一行裏三個元素等分距離,如果用百分比,那每個的寬度就是 33.33333%。這樣百分比是很長的小數,還要自己計算百分比的值,最嚴重的是經過百分比的計算後有可能最後整行寬度會留有空白。
  • 百分比的值是相對於父元素來計算的。子元素寬度的百分比是相對父元素寬度,子元素的高度的百分比是相對於父元素的高度,子元素的內外邊距都是相對父元素的寬度,這些屬性是相對誰的容易弄混。
  • 當同一行裏同時存在定寬元素和自適應的元素時,使用百分比就不太利於分配空間。
  • 如果父子元素都脫離了文檔流,那麼它父元素的寬度就可能沒有了,這時候子元素用百分比就會失效。

通過用百分比的方式適配的話,在一些情景下是非常方便的,比如一行內等距放 4 個元素,就可以每個元素 25% 的寬度,一條語句就可以搞定,但考慮到百分比適配的缺點,並不推薦所有地方都強制使用百分比進行適配。我們的項目中,用到百分比適配比較多的是需要撐開整個屏幕的情況,如頭部標題欄、導航和全屏寬的按鈕等設置寬度的時候,就可以直接給元素“width: 100%;”。

二、多套樣式分段適配

第二種適配方案就是製作多套樣式,然後根據屏幕寬度來選擇合適的那套樣式。這種適配方式一般是在 HTML 或 body 上添加一些 class 來控制。

京東的 PC 端頁面,當瀏覽器寬度大於 1190px 時,就會在 HTML 標籤上加上 o2_wide 的 class,這時候頁面的主要內容區也是定寬 1190px 的一個框子。當瀏覽器小於 1190px時,就會把 o2_wide 變成 o2_mini,這時候主要顯示區的寬度就變成了 990px。京東實際上是實現了兩種寬度的頁面,然後通過 JS 判斷頁面寬度來選擇應用哪一套。

淘寶的 PC 端會做的更精細一點,會把適配方案分成 990px、1024px、1190px、1279px、1365px、1440px 這些檔,對每一種寬度都做了一套樣式。然後也是通過 JS 控制 body 上的 class 來控制生效的樣式。

上面兩個例子都是使用 JS 來控制 DOM 元素上的 class 來實現的,在 CSS3 中還有一種控制方式叫媒體查詢。媒體查詢可以直接用 CSS 根據瀏覽器頁面寬度判斷使用哪套樣式方案。原理和上面的一樣,只不過用媒體查詢代替了上面的 JS 邏輯。

這種適配方案有如下的優點:

  • 更靈活,比如可以藉助根節點上的 class 來控制更多的內容。比如在寬屏幕上指定一行顯示 4 個元素(每個寬 25%),如果在窄屏幕上可以讓一行顯示 5 個(每個寬 20%)。
  • 頁面寬度是固定的幾檔,測試的時候比較方便。
  • 使用 JS 控制 class 的方式一般不會出現兼容性問題,通常應用在有兼容性要求的 PC 端頁面。

但這種方式的不足也比較明顯:

  • 需要製作多套設計稿,也需要多套樣式代碼,工作量比較大。
  • 要維護多套樣式,有改動的時候比較麻煩。
  • 不能在所有設備下都撐滿全屏,不適合移動端。

這種適配方案在適配時一般都是設計一套基準寬度的樣式,其他寬度下的樣式用覆蓋的方式來控制。

三、Rem 方式適配

最後就是要說 Rem 方式的適配了。在做屏幕適配的時候,通過控制 HTML 的 font-size 屬性就可以了。

第一步,限定基準尺寸屏幕下的 HTML 字號。我通常會給 HTML 節點設置上 20px 的字體,這樣做 px 到 rem 的轉換時比較容易。

第二步,在其他尺寸屏幕上,修改 HTML 字號值。在 rem 適配中,因爲只需要控制 HTML 字號就可以達到屏幕適配的目的。而在變動這個字號的時候可以像百分比適配那樣做連續的變動,直接按着屏幕寬度和標準尺寸的比例關係修改字號即可;也可以用分段適配的方式,在某一個寬度範圍內用哪一種字號。對字號的控制還是比較靈活的。

Rem 適配的優點:

  • Rem 的參考值只有一個,就是 HTML 字體大小,所以不會像百分比適配那樣要明確參考誰。
  • Rem 使用的範圍比較廣,元素的高度寬度、border 寬度、字號大小、間距和偏移量等都可以用 rem 做單位
  • Rem 適配的時候不需要做多套樣式,可以直接按比例改變 HTML字體的大小。

Rem 適配的缺點:

  • Rem 是 CSS3 的屬性,只有比較新的瀏覽器上可以使用,一般都是用在移動端。目前大廠的 H5 頁面,基本都在使用 Rem 方式做屏幕適配了。
  • Rem 最後換算出來的值還是 px,所以對於按比例分配空間這種需求是沒法滿足的,需要和其他適配方式混合使用。

四、彈性佈局適配

最後說一下彈性佈局,這也是 CSS3 裏出現的一種適配方式。彈性佈局融合了百分比佈局和固定寬度佈局的優點,並且用起來更加簡單。彈性佈局下盒模型可以不用指定寬度,直接讓盒子把可利用空間填滿,這樣既不用去計算盒子的寬度,也不用擔心固定寬度元素和需要適配的元素在同一行顯示的問題了。

彈性佈局的優點有:

  • 可以彈性地利用頁面空間,且不需要計算寬度比例。
  • 每行元素個數變化時,不用重新調整元素的寬度。
  • 彈性佈局中的高級屬性能帶來更多的功能,比如可以指定元素的排列方式和排列順序等,這些功能用傳統的 CSS 是不太好實現的。

而彈性佈局最重要的缺點就是兼容性問題,在移動端有些彈性佈局的屬性都還沒有得到完全的支持,在使用時一定要做詳細的測試。這個項目中的標題欄、底部導航、網格等組件中,都使用了彈性佈局。

CSS規範

文件引用規範

先說加載的規範,這個規範主要是爲了提高頁面加載速度或者是首屏的速度。

  • CSS 文件或樣式在 head 標籤中引用。頁面的渲染需要 CSS,所以儘量早的讓 CSS 文件加載出來。
  • JS 文件要放在 body 標籤尾部。頁面里加載和運行 JS 都會阻塞頁面的渲染過程,所以把 JS 放在尾部可以加快首屏顯示的速度,但對整個頁面完成加載的時間沒什麼影響。
  • 使用壓縮後的文件。線上使用的靜態文件,儘量都是壓縮好的,CSS 使用 .min.css 形式,JS 使用 .min.js 形式,這樣可以減少文件的體積,從而減少下載的時間。
  • 減少 import 方式引用 css 文件。import 方式引入的 CSS 文件要等原 CSS 文件加載並解析後纔會去請求,會拖慢 CSS 文件的加載速度。

選擇器的書寫規範

CSS 選擇器在使用的時候儘量遵從以下規範:

一、選擇器命名規範

  • 選擇器都用小寫字母。
  • 選擇器的命名要使用英語,這是編程的通用語言。
  • 選擇器的名字儘量簡短,但要有實際意義,不能爲了文件的體積而忽略代碼的可讀性。
  • 逗號後要有空格,一般在選擇器的逗號後面加一個空格,這樣不至於看起來擁擠。
  • 使用-分割,如果有需要多個單詞的選擇器,幾個單詞中間用-分隔,比如 page-wrap、btn-small 等。

二、選擇器使用規範

  • CSS 的屬性中需要引號的地方使用單引號,HTML 中的屬性使用雙引號。
  • CSS 選擇器在使用的時候,要把樣式限定在某個 HTML 區域裏生效。這樣可以防止不同區域的元素互相影響
    選擇器合併。有相同樣式的多個選擇器,使用分組選擇器,可以減小文件的體積。
  • 儘量不使用通配選擇器或標籤選擇器。這兩種選擇器效率比較低,儘量使用類選擇器來代替,只有在需要改變元素默認屬性的時候再使用。
  • 最右側的選擇器儘量精確。選擇器中最後一位的選擇器儘量使用類選擇器這種比較精確的選擇器,因爲選擇器的讀取是由右至左,最右邊的選擇器會先去遍歷,如果最後使用了標籤選擇器,那麼查找樣式的消耗就會增多。
  • 選擇器的嵌套不宜太長。選擇器在讀取的時候都是一層層的去查找的,所以使用太長的選擇器也會增加查找的消耗。
  • 在可以的情況下用子代選擇器代替後代選擇器。子代選擇器只需要做一層的查找,而後代選擇器需要一直查找到根節點,所以子代選擇器的效率會更高一點。
  • 使用樣式繼承。對於可以繼承的樣式,儘量在父節點加入樣式,而不要給每一個子節點都加樣式。

屬性的書寫規範

一、使用縮寫

1、屬性的縮寫。CSS 中有些屬性是可以合併的

margin: 10px 5px 0;

2、顏色的縮寫。在使用十六進制顏色的時候,如果 rgb 三個顏色位置中,每兩位的顏色值相同,可以把六位的顏色寫成三位。
3、數字的縮寫。在 CSS 中如果整數部分是 0 的小數,可以忽略小數點前面的 0;如果屬性值是 0,則可以忽略屬性值的單位。

二、屬性順序的規範

理論上,CSS 的屬性是一條一條解析執行的。這種情況下,就要把能確定大小和位置的屬性寫在前面,把對佈局沒什麼影響的屬性寫在後面,避免返工。一般說的使用順序如下:

  1. 位置屬性 (position, top, right, z-index, display, float等)
  2. 大小 (width, height, padding, margin)
  3. 文字系列 (font, line-height, letter-spacing, color- text-align等)
  4. 背景 (background, border等)
  5. 其他 (animation, transition等)

三、屬性使用的規範

  • 不大面積的使用 gif 圖片。顯示 gif 圖片的消耗比較大,所以一個頁面裏不要大面積使用 gif 圖片。
  • 儘量不要對圖片進行縮放,這也是一個高消耗的操作。
  • 減少高消耗屬性的使用 box-shadow/border-radius/filter/ 透明度 /:nth-child 等
  • 動畫裏使用 3D 屬性代替一般屬性,如使用 transform、scale 等代替原始的 width、height、margin 等,因爲這些 CSS3 的屬性可以調用 GPU 進行渲染,會減少資源的消耗並提高動畫的流暢度。

註釋規範

在代碼裏還有比較特殊的一部分就是註釋。這部分內容不參與代碼的運行,只是爲了提供給開發者看。我們對註釋有如下要求:

一、文件頭註釋。

在文件頭部加上註釋是爲了記錄文件的創建者、創建時間、最後更改者和更改時間。這樣在一個項目組裏,如果遇到開發上的問題,可以直接根據文件頭的註釋找到這個文件所屬人和操作時間。

二、普通註釋。

在業務裏也需要註釋,這種註釋我們用標準的註釋圈起來就行,最好是在註釋的文本兩邊留下個空格,這樣不會顯得擁擠。

CSS-Reset

在瀏覽器裏每個元素都有默認的樣式,也就是你不給它任何樣式也是可以顯示的。但問題是,不同的瀏覽器中默認樣式是不一致的,並且這些樣式也可能和我們的業務代碼有衝突。因此我們在開發的時候就要把這些默認樣式的差異抹平,用到的方式就是 CSS-Reset。

CSS-Reset 的做法其實就是自己寫一組 CSS 樣式,把瀏覽器的默認樣式覆蓋掉。這樣即可以抹平瀏覽器的差異,還可以自己定義元素的默認樣式。CSS-Reset 是一組公共的樣式,所以在它裏面使用標籤選擇器還是比較合適的(也有用通用選擇器的,效率更低,不推薦)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章