Functional CSS: 從試着改進可重用CSS作起

從試着改進可重用CSS作起

泊學4K視頻css

回想起每次更新泊學網站,最讓我頭疼的,就是改寫CSS。在不一樣的階段,對CSS不斷深刻的理解,對網站內容的調整,對UI的重用需求,都影響着CSS的編寫方式,所以,稍不留神,你的代碼理解就會充斥着各類風格和各類做用的CSS,讓你何時想起這些,都以爲心情不那麼愉快。佈局

所以,就和你們分享一些心得,如何理解CSS,以及如何更有效的編寫CSS。網站

從基於語義的CSS提及

首先,咱們從一個最簡單的例子開始,回想一下你的第一個CSS例子,必定和下面這樣是相似的,所謂CSS,表達的就是頁面DOM的樣式:spa

<p class="text-center">
        Hello world!
    </p>

而後,在text-center裏,咱們指定文字居中對齊的樣式:code

.text-center {
    text-align: center;
}

semantic-css-is-bad-1@2x.png

很簡單對不對?隨着樣式越寫越多,咱們很快就會開始關注到一些編寫CSS的建議。例如:應該把HTML和CSS的職責分開,HTML中不該該包含任何和具體樣式(例如居中對齊)有關的信息,這些具體的樣式都應該放到CSS中處理。視頻

因而,咱們就開始嘗試着用樣式要表達的語意來替換掉它表達的具體樣式:繼承

<p class="greeting">
    Hello world!
</p>

<style>
.greeting {
    text-align: center;
}
</style>

這樣看起來就好多了。不管.greeting指定的具體樣式是什麼,都不影響它在HTML中表示歡迎信息樣式的含義。這樣,從理論上說,咱們就能夠用一套HTML模板,實現各類不一樣風格的UI了。接口

因而,咱們就開始基於這種語義的方式,來編寫各類界面了。例如,咱們添加一個表示視頻做者的信息卡,它的HTML模板是這樣的:ip

<div class="container">
  <div class="creator-info">
    <img src="http://7xncmx.com1.z0.glb.clouddn.com/dora11.png" alt="">
    <div>
      <h2>Mars</h2>
      <p>
        The creator of boxue.io. Bla bla bla...
      </p>
    </div>
  </div>
</div>

一樣,在這個模板裏,creator-info是一個按語義命名的樣式,接下來,是這個樣式的實現:rem

.creator-info {
  background-color: white;
  border: 1px solid rgba(0,0,0,0.1);
  border-radius: 4px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  overflow: hidden;
  > img {
    display: block;
    width: 100%;
    height: auto;
  }
  > div {
    padding: 1rem;
    > h2 {
      font-size: 1.25rem;
      color: rgba(0,0,0,0.8);
    }
    > p {
      font-size: 1rem;
      color: rgba(0,0,0,0.75);
      line-height: 1.5;
    }
  }
}

它看上去的結果是這樣的:

semantic-css-is-bad-2@2x.png

這裏,咱們的重點不是這些樣式的具體內容,而是這個CSS的結構,若是咱們把全部具體的樣式都去掉,你就會發現,這個樣式嚴重依賴於HTML中DOM的層次結構:

.creator-info
  > img
  > div
    > h2
    > p

所以,儘管在HTML中,咱們依靠基於語義的樣式剝離了CSS,但這種方式卻很容易在CSS中暴露過多和HTML相關的細節。所以,這樣的作法,實際上並無徹底實現剝離CSS和HTML職責的目的,咱們須要更好的作法。

把樣式從DOM結構中剝離出來

爲了不樣式依賴DOM結構的問題,咱們的思路是:讓樣式的命名方式兼具格式和語義的功能。而後,在DOM裏,對不一樣位置的元素,使用對應的樣式。這裏,咱們借鑑了BEM命名方法,對咱們要使用的樣式名稱,統一使用這樣的命名格式:主體-依賴主體的內容__內容的屬性

<div class="container">
  <div class="creator-info">
    <img class="creator-info__image"
         src="http://7xncmx.com1.z0.glb.clouddn.com/dora11.png" alt="">
    <div class="creator-info__content">
      <h2 class="creator-info__name">Mars</h2>
      <p class="creator-info__description">
        The creator of boxue.io. Bla bla bla...
      </p>
    </div>
  </div>
</div>

此次,咱們給DOM中,每個須要樣式的元素綁定了有特定命名規則的樣式。這樣,在樣式表裏,全部的樣式就能夠是扁平結構的了:

.creator-info {
    background-color: white;
    border: 1px solid rgba(0,0,0,0.1);
    border-radius: 4px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    overflow: hidden;
}

.creator-info__image {
    display: block;
    width: 100%;
    height: auto;
}

.creator-info__content {
    padding: 1rem;
}

.creator-info__name {
    font-size: 1.25rem;
    color: rgba(0,0,0,0.8);
}

.creator-info__description {
    font-size: 1rem;
    color: rgba(0,0,0,0.75);
    line-height: 1.5;
}

還記得當時本身把泊學網站樣式修改爲這樣以後,着實興奮了一陣子,由於這樣的方式彷佛完全解決了HTML模板和CSS之間相互依賴的問題。

處理重複的界面佈局

可是沒過多久,我就發現了新的問題。當我編寫首頁上每一個視頻系列的UI組件時,結構上,它和以前的做者信息卡幾乎是同樣的。因而我幾乎不假思索的寫出了這樣的HTML模板:

<div class="container">
  <div class="series-info">
    <img class="series-info__image"
         src="https://dn-boxueio.qbox.me/YourFirstMLProject@2x-911fdc56906f05fc0757e5577084c840.jpg"
         alt="">
    <div class="series-info__content">
      <h2 class="series-info__name">Machine Learning from Scratch</h2>
      <p class="series-info__description">
        Let's create a real-world machine learning demo from scratch.
      </p>
    </div>
  </div>
</div>

它一樣包含了一個封面圖,一個標題和一個簡介。只不過,咱們把樣式名稱中的主體從creator換成了series。可是,當我要給這些新的樣式設置值的時候,就有點兒糾結了。該如何設置這些series-***的樣式呢?你可能想到了兩種選擇。

第一種,最直接的方法,就是把series-***按照creator-***複製一遍。這確定能夠工做,可是估計沒多少人會認同這種作法,由於它違反了Don't Repeat Yourself的原則;

第二種,若是你使用了SCSS,就能夠實現從某個樣式繼承這樣的用法:

.series-info {
    @extend .creator-info;
}

.series-info__image {
    @extend .creator-info__image;
}

.series-info__content {
    @extend .creator-info__content;
}

.series-info__name {
    @extend .creator-info__name;
}

.series-info__description {
    @extend .creator-info__description;
}

但這樣作也有它本身的問題,@extend應該只在彼此有關聯的樣式之間使用,而不只僅是爲了不重複編寫相同的樣式。而且,若是稍後咱們還要視頻信息卡呢?真的須要這些使用了相一樣式的selector麼?顯然,目前的這種解決方案仍舊不夠理想。

去除掉過於細緻的語義

實際上,形成樣式難以重用的緣由,是由於selector表達的語義過於細緻了。語義越細緻,重用就越困難。所以,咱們只要把這種綁定相似界面佈局UI的selector,起個名字替代掉相似creatorseries這樣的名字就行了:

.media-card {
  background-color: white;
  border: 1px solid rgba(0,0,0,0.1);
  border-radius: 4px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  overflow: hidden;
}

.media-card__image {
    display: block;
    width: 100%;
    height: auto;
}

.media-card__content {
    padding: 1rem;
}

.media-card__name {
      font-size: 1.25rem;
      color: rgba(0,0,0,0.8);
}

.media-card__description {
      font-size: 1rem;
      color: rgba(0,0,0,0.75);
      line-height: 1.5;
}

這樣,不管是做者信息仍是視頻系列信息,就均可以用同一套樣式來表示了:

<div class="container">
  <div class="media-card">
    <img class="media-card__image"
         src="https://dn-boxueio.qbox.me/YourFirstMLProject@2x-911fdc56906f05fc0757e5577084c840.jpg" alt="">
    <div class="media-card__content">
      <h2 class="media-card__name">Machine Learning from Scratch</h2>
      <p class="media-card__description">
        Let's create a real-world machine learning demo from scratch.
      </p>
    </div>
  </div>
</div>

甚至,只要UI佈局和media-card描述的體系相同,這套樣式就能夠直接重用。

可是情到此結束了麼?顯然沒有,如今,你可能又會想了:假設咱們須要修改做者信息卡的樣式,但仍保存視頻系列信息卡的樣式該怎麼辦呢?

若是像以前同樣,它們的樣式是獨立的,只修改對應的樣式就行了。如今,它們共享樣式了,我不只要建立新的樣式,還要連同對應的HTML一塊兒修改,這樣作真的好麼?

實際上根本沒有絕對的職責分離

爲了回答這個問題,咱們得回到這一節開始提出的目的:分離HTML和CSS的職責。面對這個話題,咱們直覺上就會認爲,只有完全剝離了纔算完成達成目標。但實際的狀況則是,它們二者根本沒法作到徹底分離。咱們只能根據本身項目的實際狀況,選擇一種適合本身的方式。

對於哪些具有詳細語義(.creator-info.series-info)的樣式而言,此時,HTML是獨立的,它徹底不關心這些DOM會長成什麼樣子。它只暴露了一個接口,容許咱們定製其中的樣式。所以,這種選擇下的CSS不是獨立的,它依賴於樣式綁定的HTML,須要以HTML爲參考,定義樣式的內容。

對於那些具有中立語義(.media-card)的樣式而言,此時,CSS是獨立的,它徹底不關心本身會被用在什麼元素上。此時,HTML就不是獨立的了,它須要知道樣式表提供了哪些內容,並基於這些內容,來編排DOM。

實際上,這兩種方法,沒有絕對的誰優誰劣的問題。只是你要想清楚,哪一種方式更適合本身的項目。

What's next?

看到這裏,若是你和我以前有過相似的困惑,如今,你應該躍躍欲試地要調整下本身的CSS了。先彆着急,在下一節裏,咱們將繼續討論,如何經過合理的命名,最大化實現樣式的可重用目標。

相關文章
相關標籤/搜索