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了。先别着急,在下一节里,咱们将继续讨论,如何经过合理的命名,最大化实现样式的可重用目标。

相关文章
相关标签/搜索