css架構初探

碼道人 2021-08-15 20:42:04 阅读数:256

本文一共[544]字,预计阅读时长:1分钟~
css 初探

logo

在實現業務的過程中,我們難免會發現之前由於各種原因存在的代碼中正在產生大量的冗餘。這時候就需要優化代碼,如果有功能的迭代,就是進行重構的好時機了!

我曾看過很多關於架構的書籍、文章。恕我膚淺,至今都沒能確切搞懂架構的真正含義,只是覺得:這是一個通過某些手段將項目變成一個層級劃分、面向功能、結構清晰的關系。

最近在搞某一塊業務的重構,恰好時間不緊。組內大佬說“放手去幹吧”。於是我將目光投向了自認為比較了解的CSS方面。

ITCSS

這是由csswizardry提倡的一個 CSS 設計方法論,他可以讓你更好的管理、維護你的項目的 CSS。

它可以幫助你

  • 管理 CSS 代碼的書寫順序
  • 過濾明確性?估計是說分層來明確每層 CSS 的作用
  • 馴服 CSS cascade(權重)
  • 安全的使用繼承

ITCSS 把 CSS 分成了以下的幾層:

  • Settings | 項目使用的全局變量
  • Tools | mixin & function
  • Generic | 最基本的設定 normalize.css,reset
  • Base | type selector
  • Objects | 不經過裝飾 (Cosmetic-free) 的設計模式
  • Components | UI 組件
  • Trumps | helper 唯一可以使用 important! 的地方

也就是常說的“七層架構”。

但在實際項目中,我們只需借鑒其思路,達到維護一套完善利於閱讀、擴展、複用的css代碼即可。

我是怎麼做的

首先,上面說的第一層 —— Settings 是很重要的。我們可以在其中放一些公共css變量。比如負責更改主題色的變量。常見的有:

  • 顏色
  • 邊框
  • 字體大小
  • 陰影
  • 層級
  • 排版
  • ...
/* Color ----------------------- */
$color-primary: #FF5777;
$color-white: #FFFFFF;
$color-black: #000000;
$color-text-primary: #333333;
$color-text-secondary: #666666;
$color-text-tertiary: $color-white;
$color-text-quaternary: $color-primary;
$border-color-base: #E5E5E5;
$background-color-primary: #F1F1F1;
$background-color-secondary: $color-white;
$background-color-tertiary: $color-primary;
/* Border ----------------------- */
$border-width-base: 1Px;
$border-style-base: solid;
$border-base: $border-width-base $border-style-base $border-color-base;
/* z-index -------------------------- */
$index-normal: 1;
$index-top: 1000;
$index-popper: 2000;

注意:這裏必須是抽取的全局的、多個地方會使用到的公共的樣式變量。更加細節的可以放在後面層級中單獨寫。

但通常,主題色不是這麼容易實現的。它甚至需要大量的函數計算以及js的介入 —— 目前流行的elementUI就是在 Base 層下又加了一層(這也是為什麼選擇ITCSS方案的原因:隨意擴展和縮减)。

然後是 tools 層,也是不可或缺的。這裏面經常被用來放一些“工具樣式”:比如當你使用了scss後的一些需要全局處理的mixin函數、比如水平垂直居中、比如溢出省略、清除浮動等樣式類或function ...

關於這一層,網上有好多人推薦_sassMagic.scss庫。據說挺好用的!

值得注意的是:上面兩層都是 全局層面 的。一般筆者是這樣安排的:(在我司的大部分項目中,這兩層都屬於自研脚手架中內置的)

1

這裏說一句題外話:其實原生 css 越想寫簡單(提高複用)就越會發現,如果 css 中能引用 css 就好了(非@import形式) —— 這樣 css 的東西就可以在 css 內部解决,完成一次複用。到 html 中只需引入一個類/屬性名即可!scssmixin就達到了這個效果。

@mixin large-text {
color: #737373;
}
.line-title {
@include large-text;
padding: 4px 0;
}
.code-title {
@include large-text;
padding: 2px;
}

(css的自定義變量也可以達到這樣的效果 —— 不過你要寫在:root中。而且 scss 中的自定義變量更加强大!)

這一層推薦Ant Design 庫,它的mixin寫的非常之精妙。

注意️:這兩層的主要代碼(涉及創建了變量、mixinfunction的)是要在vue.config.js中引入的 —— 這樣就能在其它css和頁面中使用到:

module.exports = {
css: {
loaderOptions: {
scss: {
prependData: `
@import "@/style/settings/var.scss";
@import "@/style/theme/scss/index.scss";
@import "@/style/tools/_sassMagic.scss";
`
},
}
}
}

第三層 Generic。這一層就是專門放置一些css樣式初始化等功能。你可以選擇normalize.css這樣成型的第三方css;也可以根據項目中用到的標簽做針對性處理。

這一層沒什麼說的。

第四層 Base 層:這一層可以用來放定制化css樣式。比如你的網站中 a 鏈接點擊後是什麼樣、或者 li 的前面幾個點是什麼樣的。

這兩層是 組件級別 的。

一般來說,“全局級別”的樣式主要負責供應“全局”、“其它低級別樣式文件”以及“極少量獨立樣式代碼”;而“組件級別”主要負責供應所有構成頁面的組件中需要的樣式、制定本項目樣式規範以及特殊情况。

第五層 Object 和第六層 Components 其實可以合並為一層:component。它其實就是寫組件 —— 你完全可以理解為你在書寫vue組件!

這一層首先在結構上不再維護在和其它目錄同級的目錄下(如上面的style),而是放在組件存放的 components 目錄下。

在這一層你要做的就是:自行/利用第三方庫封裝一個具有“基本架子(結構)”的組件。考慮到複用性,所以這裏使用最多的就是slot了。比如:

<!-- src/components/layout/footer.vue -->
<template>
<footer class="c-footer">
<slot></slot>
</footer>
</template>
<script> export default { name: 'VFooter' } </script>
<style lang="scss" scoped> @include b(c-footer) { position: fixed; bottom: 0px; width: 100%; } </style>

由於這一層的“特殊性”,再加上根據 css 中的就近原則來說他們對html的影響是最大的,也是最小的(只負責一個文件的樣式,一般一個文件就是一個部分的功能)。所以推薦OOCSS(面向對象css)的進階寫法:BEM

BEM規範

場景一:開發一個彈窗組件,在現有頁面中測試都沒問題,一段時間後,新需求新頁面,該頁面一打開這個彈窗組件,頁面中樣式都變樣了,一查問題,原來是彈窗組件和該頁面的樣式相互覆蓋了,接下來就是修改覆蓋樣式的選擇器...又一段時間,又開發新頁面,每次為元素命名都心驚膽戰,求神拜佛,沒寫一條樣式,F5都按多幾次,每個組件都測試一遍...

場景二:承接上文,由於頁面和彈窗樣式沖突了,所以把頁面的沖突樣式的選擇器加上一些結構邏輯,比如子選擇器、標簽選擇器,借此讓選擇器獨一無二。一段時間後,新同事接手跟進需求,對樣式進行修改,由於選擇器是一連串的結構邏輯,看不過來,嫌麻煩,就幹脆在樣式文件最後用另一套選擇器,加上了覆蓋樣式...接下來又有新的需求...最後的結果,一個元素對應多套樣式,遍布整個樣式文件...

以往開發組件,我們都用“重名概率小”或者幹脆起個“當時認為是獨一無二的名字”來保證樣式不沖突,這是不可靠的。

理想的狀態下,我們開發一套組件的過程中,我們應該可以隨意的為其中元素進行命名,而不必擔心它是否與組件以外的樣式發生沖突。

BEM解决這一問題的思路在於,由於項目開發中,每個組件都是唯一無二的,其名字也是獨一無二的,組件內部元素的名字都加上組件名,並用元素的名字作為選擇器,自然組件內的樣式就不會與組件外的樣式沖突了。

這是通過組件名的唯一性來保證選擇器的唯一性,從而保證樣式不會污染到組件外。

這也可以看作是一種“硬性約束”,因為一般來說,我們的組件會放置在同一目錄下,那麼操作系統中,同一目錄下文件名必須唯一,這一點也就確保了組件之間不會沖突。

BEM的命名規矩很容易記:block-name__element-name--modifier-name,也就是模塊名 + 元素名 + 修飾器名

這裏面還涉及到一個問題:要不要用scope?這個問題值得深思,比如vue中的scoped會形成一個樣式隔離。如果需要樣式複用還需要樣式穿透的介入,非常麻煩。但是一味的遵循“開放”反而會引來“無妄之灾”。

OOCSS中,最重要的便是“結構與皮膚分離”。結構就是指“基礎對象”,也就是我們說的“搭好一個架子”。

<div class="media">
<div class="m-img"></div>
<span class="m-content"></span>
</div>

遇到上面的HTML,一般會先給一個“固定的樣子”:

.media {
.m-img {
}
.m-content {
}
}

這時候如果有新的樣式或者顏色之類的改動。就需要另寫一個類名:

<div class="media m-color">
<div class="m-img"></div>
<span class="m-content"></span>
</div>
.m-color {
color: red;
}

但是在其它組件中,就完全不受影響:

<div class="media">
<div class="m-img"></div>
<p class="m-content"></p>
</div>

OOCSS的複用就是體現在“架子的重複使用”上。這一點和“組件複用”有异曲同工之妙!

(事實上,第三方組件的使用也可以這麼做:比如對於vant的栅格布局,如果你的項目中大量使用到了它,就可以在全局引入,並修改樣式。這也是vue優化的一個點)

ACSS規範

有時候我們還會在 component 上(樣式的優先級比component低)再加一層:ACSS。

ACSS是原子類樣式。通俗的講就是“一個類只寫一個樣式”。這樣的好處是可以達到對css的極限複用。而不好的地方就是讓css失去了語義化

可以用屬性選擇器解决無語義化的痛點。

在 scss 强大的函數加持下,比如你寫不同透明度的background可以這樣:

/** 背景顏色 [bgaxxx] { background-color: rgba(0, 0, 0, 0.xxx); } */
@each $i in 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1 {
[bga#{$i * 10}] {
background-color: rgba(0, 0, 0, $i);
}
}

最後一層 Trumps ,其實就是在你的組件中進一步“描述”功能的差异性。結合之前層次樣式的複用展現想要的效果。

為什麼架構

css架構的目的不是“為了在頁面不寫css代碼”,而是“為了更好的複用,更簡單的維護,和更清晰的結構”!

在上面的內容中,很明顯看到:

  • “層級越靠前,優先級越低、複用性越强”
  • “下一層永遠繼承上面(所有)層”
版权声明:本文为[碼道人]所创,转载请带上原文链接,感谢。 https://gsmany.com/2021/08/20210815204156371C.html