如何編寫通用的 Helper Class

Github: https://github.com/nzbin/snack-helper

Docs: https://nzbin.github.io/snack-helpercss

前言

什麼是 helper ?任何框架都不是萬能的,而業務需求倒是多種多樣,不少時候咱們只須要更改組件的部分屬性,而 helper 就是調整細節的工具。我在以前的文章《如何編寫輕量級 CSS 框架》中也舉過例子,咱們徹底不必由於幾個屬性的不一樣而從新編寫新組件。大部分的 helper 都是一個類對應一個 CSS 屬性,屬於最細小的類。經過工做的實踐總結,我以爲編寫一套簡單易用、通俗易懂的 helper 很是重要。本文的目的就是探討 helper 的組成部分、編寫方式以及如何精簡 helper 的命名。html

組件與零件

詳細介紹如何編寫 helper 以前,先說一下我對於組件以及零件的見解。在以前編寫輕量級 CSS 框架的時候,咱們是以組件的方式開發。而編寫 helper 更像是開發一個零件,由於 helper 的屬性單一,並且多個 helper 能夠造成一個組件。好比下面的例子:git

假設有 .boxes 組件github

.boxes {
    border: 1px solid #eee;
    border-radius: 5px;
    margin-bottom: 15px;
    overflow: hidden;
}

假設有以下 helper框架

.b-1 {
    border: 1px solid #eee !important;
}
.r-5{
    border-radius: 5px !important;
}
.m-b-15{
    margin-bottom: 15px !important;
}
.overflow-hidden {
    overflow: hidden !important;
}

.boxes = .b-1 + .r-5 + .m-b-15 + .overflow-hidden函數

我是一個模型愛好者,這樣的組合方式讓我想到了壽屋的 HEXA GEAR 系列模型,這個系列的特色是「<span>零件+零件=組件、組件+組件=骨架、骨架+骨架=素體、素體+武裝=機體</span>」。工具

在編寫 helper 的時候,基於以上想法,我在思考是否能夠把 helper 拆分的足夠精細,這樣它就能夠自成一體造成一個框架,也就是「零件+零件=組件、組件+組件=框架」。使人遺憾的是,個人想法已經被人實踐,前幾天瀏覽 GitHub 時發現了相關的項目 tailwindcss,這個框架就是以 helper 爲基礎,經過屬性疊加的方式添加樣式。佈局

組件式框架和零件式框架是兩種徹底不一樣的思想,難分伯仲,各有優缺點。性能

Helper 的組成部分

一套完整的 helper 應該包含哪些內容呢?通常經常使用的有 paddingmarginfont-sizefont-weight 等。爲了編寫更爲通用的 helper,咱們須要更細緻的劃分。雖然咱們並無打算把它寫成一個框架,可是咱們但願 helper 的功能足夠強大。經過對比和思考,我將 helper 暫時劃分紅如下幾個模塊:測試

  • Colors(顏色,包括 bg-color 及 text-color)
  • Paddings(內邊距序列)
  • Margins(外邊距序列)
  • Typography(排版,包括 font-size 及 font-weight)
  • Border(邊框線)
  • Radius(圓角)
  • Shadow(陰影)
  • Size(尺寸,包括 height 及 width)
  • Gutters(柵格間距序列)
  • Alignment(主要是 vertical-align)
  • ...

和以前編寫輕量級框架同樣,咱們一樣使用 Sass 預編譯器。helper 類幾乎都是 Sass 循環生成的,因此源代碼看上去很精簡。

顏色變量

由於顏色稍微特殊一點,我將顏色與其它內容分開單獨介紹。在編寫輕量級框架的時候,我也定義了經常使用的一些顏色,可是面對特殊需求時略顯單一,因此咱們須要使用 helper 擴充顏色集羣。可是顏色是一個沒法量化的概念,因此再強大的 helper 也沒法面面俱到,只能是必定程度上的補充。參考經常使用的顏色值,最終我設置了紅、橙、黃、綠、青、藍、靛、紫、粉、冷灰、暖灰等幾種色系。

其中每一個顏色都有六個亮度值,分別用 -lightest-lighter-light-dark-darker-darkest 表示,此處有參考 tailwindcss 的顏色命名。這些顏色都是經過 Sass 的顏色函數生成的。以灰色爲例,Sass 代碼以下:

$gray:#999;
$gray-light:lighten($gray, 15%);
$gray-lighter:lighten($gray, 25%);
$gray-lightest:lighten($gray, 35%);
$gray-dark:darken($gray, 15%);
$gray-darker:darken($gray, 25%);
$gray-darkest:darken($gray, 35%);

這些顏色序列看上去很像一套馬克筆,不過馬克筆灰色系更豐富,包括冷灰、暖灰、藍灰、綠灰。

其中背景色的循環方式以下,爲了便於循環,咱們定義了一個 color map,而後用 @each 方法循環。

$color-list:(
    'gray':$gray,
    'brown':$brown,
    'red':$red,
    'orange':$orange,
    'yellow':$yellow,
    'green':$green,
    'teal':$teal,
    'blue':$blue,
    'indigo':$indigo,
    'purple':$purple,
    'pink':$pink
);

@each $name,$color in $color-list {
    .bg-#{$name} {
        background-color: $color;
    }
    .bg-#{$name}-light {
        background-color: lighten($color, 15%);
    }
    .bg-#{$name}-lighter {
        background-color: lighten($color, 25%);
    }
    .bg-#{$name}-lightest {
        background-color: lighten($color, 35%);
    }
    .bg-#{$name}-dark {
        background-color: darken($color, 15%);
    }
    .bg-#{$name}-darker {
        background-color: darken($color, 25%);
    }
    .bg-#{$name}-darkest {
        background-color: darken($color, 35%);
    }
}

命名策略

理所固然,我又提到了命名策略。在編寫輕量級框架的時候,我也着重討論了類命名策略以及比較了一些框架的命名方式。不管是框架仍是 helper,類命名都決定了其易用性,並且會影響使用者的習慣,因此我會從簡潔、直觀、易用等幾個角度命名。不過 helper 的命名比較簡單,由於幾乎大多數都是單一的 CSS 樣式,因此命名策略基本都是對 CSS 屬性的抽象與簡化。

數字型命名 VS. 尺寸型命名

我在工做中接觸過兩種 helper 序列的表示方法,一種是常見的數字型,另外一種是尺寸型。以 padding 爲例:

數字型

.p-5 {
  padding: 5px !important;
}
.p-10 {
  padding: 10px !important;
}
.p-15 {
  padding: 15px !important;
}
.p-20 {
  padding: 20px !important;
}
.p-25 {
  padding: 25px !important;
}

尺寸型

.p-xs {
  padding: 5px !important;
}
.p-sm {
  padding: 10px !important;
}
.p-md {
  padding: 15px !important;
}
.p-lg {
  padding: 20px !important;
}
.p-xl {
  padding: 25px !important;
}

雖然在實際應用時,尺寸型寫法並無什麼不妥,但很明顯它的擴展性不好,並且不直觀。做爲例子,我只寫了五個數值,但若是咱們但願添加更多的 padding 值的話,尺寸型命名就乏力了。我認爲,凡是能夠量化的屬性,好比 paddingmarginfont-sizeborder-width 等,應該直接用數值表示,而對於不能夠量化的屬性,好比 box-shadow,用尺寸型命名比較合適。

精簡命名

大多數的 helpr 命名都是 CSS 屬性的首字母縮寫形式。好比 p 表示 paddingm 表示 marginf-s 表示 font-size 等。這符合咱們指望的簡潔直觀的要求。但也不能惟縮寫論,全部的命名都用縮寫,由於有些屬性的縮寫會重複,並且有些縮寫以後就不知道具體含義了。咱們能夠沿用以前的規則,能夠量化的屬性都用縮寫,不能夠量化的屬性用簡化的全稱(好比 box-shadow 能夠替換爲 shadow)。

以 padding 循環爲例:

@for $counter from 0 through 6 {
    .p-#{ $counter * 5 } {
        padding: ($counter * 5px) !important;
    }
    .p-t-#{ $counter * 5 } {
        padding-top: ($counter * 5px) !important;
    }
    .p-r-#{ $counter * 5 } {
        padding-right: ($counter * 5px) !important;
    }
    .p-b-#{ $counter * 5 } {
        padding-bottom: ($counter * 5px) !important;
    }
    .p-l-#{ $counter * 5 } {
        padding-left: ($counter * 5px) !important;
    }
}

對於其它幾個 helper 與此相似,循環也很簡單。

關於 Margin 負值

margin 的 helper 相比其它來講比較特殊,由於它有負值,因此咱們必須考慮如何表示負值。有些框架用 n (negtive)表示負值。好比 m-{t,r,b,l}-n-* 的形式:

.m-t-n-5 {
  margin-top: -5px !important;
}
.m-r-n-5 {
  margin-right: -5px !important;
}
.m-b-n-5 {
  margin-bottom: -5px !important;
}
.m-l-n-5 {
  margin-left: -5px !important;
}

我以爲徹底能夠簡化一步,用 - 表示負值,簡單易懂,以下:

.m-t--5 {
  margin-top: -5px !important;
}
.m-r--5 {
  margin-right: -5px !important;
}
.m-b--5 {
  margin-bottom: -5px !important;
}
.m-l--5 {
  margin-left: -5px !important;
}

雖然這種命名方式很簡潔,但看上去和其它 helper 不太統一。

關於圓角

圓角的 CSS 屬性名爲 border-radius,若是直接簡寫的話和 border-right 就重複了,參見其它框架的表示方法有 corner-roundedrounded 等。咱們也能夠簡化一下,好比直接用 r 表示,既能夠表明  rounded 也能夠表明 radius,一箭雙鵰。這樣的表示方法應該不會有歧義,畢竟在咱們的腦海中,r 表示半徑算是一個根深蒂固的概念。Sass 代碼以下:

@for $counter from 0 through 10 {
    .r-#{ $counter } {
        border-radius: ($counter * 1px) !important;
    }
    .r-t-l-#{ $counter } {
        border-top-left-radius: ($counter * 1px) !important;
    }
    .r-t-r-#{ $counter } {
        border-top-right-radius: ($counter * 1px) !important;
    }
    .r-b-r-#{ $counter } {
        border-bottom-right-radius: ($counter * 1px) !important;
    }
    .r-b-l-#{ $counter } {
        border-bottom-left-radius: ($counter * 1px) !important;
    }
}

咱們用 -full 表示 100%,其它框架也基本如此,稍後再談論 r-100% 這種形式的可行性及問題所在。

.r-full {
    border-radius: 100%
}
.r-t-l-full {
    border-top-left-radius: 100%
}
.r-t-r-full {
    border-top-right-radius: 100%
}
.r-b-r-full {
    border-bottom-right-radius: 100%
}
.r-b-l-full {
    border-bottom-left-radius: 100%
}

一樣的,高度和寬度的 100% 數值也用 -full 表示,循環方式相似。

關於陰影

咱們在以前反覆提到了陰影屬於非量化的屬性,因此只能使用尺寸型命名法,固然用數字也不是不能夠,一下子再詳細說明。先看源代碼:

.shadow-xs{
    box-shadow:0 1px 5px 1px rgba(0,0,0,.15);
}
.shadow-sm{
    box-shadow:0 2px 10px 2px rgba(0,0,0,.15);
}
.shadow-md{
    box-shadow:0 3px 20px 3px rgba(0,0,0,.15);
}
.shadow-lg{
    box-shadow:0 4px 30px 4px rgba(0,0,0,.15);
}
.shadow-xl{
    box-shadow:0 5px 40px 5px rgba(0,0,0,.15);
}

總體而言,比較簡潔,不過陰影的數值我是粗略添加的,實際狀況要作調整。說點題外話,我我的以爲對於非量化的屬性自己而言,或許用處就不大,由於這些屬性可以知足業務需求的可能微乎其微,可是它仍然是不可獲取的一部分。因此說「通用的」 helper 並不必定通用。

關於強度表示法

經過 font-weight 說一下關於強度的表示法,font-weight 的 CSS 屬性自己就有兩種表示法,一種是直接文字命名,好比 .f-s-thin , .f-s-normal, .f-s-bold 等,另外一種是比較直接的 100 ~ 900 數值型表示法。以我我的觀點,我更傾向於數值型表示法,簡單直觀,並無歧義,也算是約定俗成的規定吧。font-weight 的循環比較簡單,並且數值有限,咱們能夠直接寫出從 100 ~ 900 的全部 helper。其它相似的 helper 也能夠用 100 ~ 900 表示強度,好比顏色。

須要注意的是,編寫 helper 時必定要對數值型、尺寸型、強度型命名作好歸類與統一,切記毫無章法地胡亂使用。

類命名中的特殊字符

對於 r-100% 或者 w-100% 這樣的寫法是能夠的,可是在定義 CSS 時要進行字符轉義,好比

.r-100\% {
    border-radius: 100%
}

使用方式以下

<div class="r-100%"></div>

可是這種寫法總給人怪怪的感受,並且輸入時要按 shift + %,不太方便,因此暫時只做爲參考。
另外說明一點,咱們能夠經過特殊字符定義百分數,好比:

.w-50 {
   width: 50px;
}
.w\:50 {
   width: 50%
}

經過約定的這種規則,咱們就能夠爲 helper 添加柵格系統了。不過這只是暫時的想法,畢竟咱們已經有一套輕量級 CSS 框架了。

序列數量

由於 helper 是循環生成的,因此循環的數量決定了 helper 的豐富度。那麼循環的數量多少合適呢?這是全部 helper 最難統一的地方。不能否認,helper 的數量越多,通用性越強,也就越靈活。任何事物都有兩面性,雖然 helper 越多越好,可是數量太多會形成文件臃腫。目前我寫的 helper 的文件體積幾乎和以前的輕量級框架差很少,某種程度上來講確實在向「零件化」的框架發展。另外一方面,其實 helper 並無必要寫的太全面,不少數值會形成冗餘。

簡單來講,對於有限值的 helper 就能夠所有寫出,好比對其方式、font-weight 等。而對於任意數值的 helper 來講,咱們須要選擇經常使用的一些數值,好比 paddingmargin 等屬性,基本 1~50 px 之間就能夠了,而圓角 1~20 px 足矣。不能量化的屬性好比陰影就徹底看我的喜愛了,我以爲五個尺寸就差很少。對於實在特殊的需求也只能特殊對待了。

演示

如今咱們測試一下咱們所寫的 helper 是否是可以知足通常需求,好比一個帶有圓角陰影的用戶卡片,以下:

See the Pen snack-helper-test

這個實例所有是用 helper 完成的,惋惜這套 helper 沒有柵格系統,因此佈局並不靈活,可是結合以前的輕量級框架,會顯示出它強大的功能。

總結

編寫 helper 比編寫框架要容易的多,但簡單易用、通俗易懂的 helper 還須要嚴謹的思考,詳細的 helper 能夠參見 GitHub 源碼。雖然我一直聲稱沒有打算把 helper 寫成一個框架,但隨着細節的追加與調整,好比添加柵格系統,這個通用的 helper 已經趨向於一個「零件化」的框架了。至於組件式框架和零件式框架哪一個更好,這是一個很難選擇的問題。可是我更傾向於組件與零件的結合,由於我不但願整個 HTML 文件被冗長的 CSS 類裝飾的支離破碎。

相關文章
相關標籤/搜索