淺析CSS中的5種設計模式,聊聊vue專案中CSS目錄程式碼的作用

2022-02-08 13:01:04
本篇文章帶大家聊聊CSS中的5種設計模式,並介紹一下vue3專案中個CSS style目錄中的程式碼作用,希望對大家有所幫助!

工作了幾年,發現在專案中經常存在如下問題:

  • 1.模組拆分不合理
  • 2.變數和函數命名不知所云
  • 3.缺少註釋或者是寫了一堆描述不清的內容
  • 4.重複的程式碼遍佈各個角落等

因為這些不良的程式設計習慣,導致了專案越來越難以維護,程式效能越來越低,大大降低了日常的工作效率以及提高了公司的開發成本。

下面就以CSS在Vue3專案中的架構為切入點,通過減少CSS程式碼的冗餘度和增強CSS程式碼的維護性、擴充套件性來提高我們的程式設計能力和專案架構能力。

技術儲備:

  • Sass(https://www.sass.hk/docs/)
  • Vue3(https://v3.cn.vuejs.org/)

CSS的設計模式

在學習CSS架構之前,我們先簡單看一下常見的5種CSS設計模式,這些設計模式都為我們的CSS架構提供了一定的開發思路。

1.OOCSS模式

OOCSS(Object-Oriented CSS)字面意思是物件導向的CSS,在開發中它有如下的規範約定

  • 減少對 HTML 結構的依賴
# bad
# 1.匹配效率低,影響css效能
# 2.和html耦合度高,維護性和擴充套件性低
.container-list ul li a {}

<div class="container-list">
  <ul>
    <li>
      <a>...</a>
    </li>
  </ul>
</div>


# good
.container-list .list-item {}

<div class="container-list">
  <ul>
    <li>
      <a class="list-item">...</a>
    </li>
  </ul>
</div>
  • 增加樣式的複用性
.label {
  # 公共程式碼
}
.label-danger {
  # 特定程式碼
}
.label-info {
  # 特定程式碼
}
<div>
  <p class="label label-danger"></p>
  <p class="label label-info"></p>
</div>

2.BEM模式

BEM 是進階版的OOCSS,是一個分層系統,它把我們的網站分為三層,這三層正好對應著 BEM 三個英文單詞的簡寫 block, element, modifier,分為為 塊層、元素層、修飾符層。

把 BEM 體現到程式碼上,我們需要遵循三個原則:

  • 使用__兩個下劃線將塊名稱與元素名稱分開
  • 使用--兩個破折號分隔元素名稱及其修飾符
  • 一切樣式都是一個類,不能巢狀。
<div class="menu">
  <div class="menu__tab menu__tab--style1">tab1</div>
  <div class="menu__tab menu__tab--style1">tab2</div>
</div>

但是,由於兩個下劃線__和兩個破折號--在實際開發中不是那麼的順手,影響開發效率,不過要嚴格控制CSS命名規範的話,這無疑是一個好的選擇。並且在寫CSS的時候我們可以通過Sass的混合指令封裝一個BEM.scss檔案來減少類名的輸入以及增強CSS結構

3.SMACSS模式

BEM 簡單的三層分法,在應對小中型網站沒有問題,但是去應對複雜網站的樣式可能就比較困難了,我們需要尋求一個更好的辦法。

SMACSS(Scalable and Modular Architecture for CSS)是要編寫模組化、結構化和可延伸的 CSS。它對專案中的CSS分為五大類

  • Base: 預設屬性樣式重置,知名庫為normalize.css
  • Layout:佈局樣式
  • Modules:可複用模組的樣式,比如一些列表展示
  • State:狀態樣式,比如按鈕的置灰或高亮的展示
  • Theme:面板樣式,比如有些網站具有換膚的功能

4.ITCSS模式

ITCSS(Inverted Triangle Cascading Style Sheets)可以翻譯為"倒三角CSS",它基於分層的概念把我們專案中的樣式分為七層

  • Settings: 專案樣式變數,如主題色、字型等
  • Tools:工具類樣式,比如定義一個函數,表示字數過多出現省略號等
  • Generic:重置和/或標準化樣式、框大小定義等,對應的是normalize.css
  • Base:重置瀏覽器元素屬性預設值
  • Objects:維護OOCSS的樣式
  • Components:公共元件樣式
  • Trumps:讓樣式權重變得最高,實用程式和輔助類,能夠覆蓋三角形中前面的任何內容,唯一 important! 的地方

5.ACSS模式

ACSS(Atomic CSS)翻譯為"原子化CSS",是一種 CSS 的架構方式,它傾向於小巧且用途單一的 class,並且會以視覺效果進行命名。是一個不強調邏輯,而更側重表現的一門所見即所得的語言,它出現的背景是——前端元件化時代的到來,各個元件的CSS可以做到互相獨立,互不影響。因此就有這樣的程式碼出現

<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">按鈕</button>

目前市場上比較成熟的ACSS庫有:Tailwind CSSWindi CSS

ACSS的優點

  • CSS檔案停止增長:使用傳統方法,每次新增新功能時,您的 CSS 檔案都會變大。使用實用程式,一切都是可重用的,因此您很少需要編寫新的 CSS,一套樣式全域性通用。
  • 不再浪費精力命名,不再新增愚蠢的類名:例如 sidebar-inner-wrapper 只是為了能夠設定樣式,也不再為真正只是一個 flex 容器的東西的完美抽象名稱而苦惱。
  • 靈活,易讀:CSS 是全球性的,當你做出改變時,你永遠不知道你破壞了什麼。HTML 中的類是原生的,因此可以 插拔式改變樣式 而不必擔心其他問題,CSS 樣式很多縮寫更加符合大腦的記憶。
  • 永遠不用擔心命名衝突,永遠不用擔心樣式覆蓋。

ACSS的缺點

  • 會增加HTML 的體積
  • 破壞了CSS命名的語意化
  • 熟悉命名 ACSS 命名會有一定成本

綜上,我們可以看出ACSS 劣處是非常小的,而好處有非常大,沒有理由在專案中不適用。下面我們通過使用BEM、ITCSS和ACSS模式打造一套CSS架構方案。

專案搭建

建立vue3專案和安裝依賴

CSS目錄結構展示與說明

src
  style
    acss         # 存放boder、margin、padding等基於acss模式的程式碼
    base         # 存放元素(input、p、h1等)的重置樣式
    settings     # 存放專案統一規範的文字顏色、邊框顏色等變數
    theme        # 存放專案特定主題下的元素樣式
    tools        # 存放封裝好的mixin(混合指令)和function(函數)樣式
    global.scss  # 需要專案全域性參照的CSS
    index.scss   # 需要Vue檔案參照的CSS

1.關於mixin(混合指令)和function(函數)的區別

  • 函數是有計算邏輯,返回計算的結果,不輸出css塊
  • mixin主要是根據計算結果輸出css塊
/* mixin */
@mixin center-translate($direction: both) {
  position: absolute;
  @if $direction == both {
    top: 50%;
    left: 50%;
    transform: translate3d(-50%, -50%, 0);
  } @else if $direction == horizontal {
    left: 50%;
    transform: translate3d(-50%, 0, 0);
  } @else if $direction == vertical {
    top: 50%;
    transform: translate3d(0, -50%, 0);
  }
}

/* function */
@function am($module, $trait: false) {
  @if $trait==false {
    @return '[am-' + $module + ']';
  } @else {
    @return '[am-' + $module + '~="' + $trait + '"]';
  }
}

2.關於style/global.scss和style/index.scss

  • global.scss中匯入的程式碼不僅在Vue檔案中使用,而且在style中scss定義檔案裡也會被參照到
# style/global.scss
@import "./settings/var.scss";

# style/settings/var.scss
$background-color-primary: #F1F1F1;
$background-color-secondary: $color-white;

# style/acss/color.scss
@each $style in (primary $background-color-primary, secondary $background-color-secondary) {
  [bg-#{nth($style, 1)}] {
    background-color: #{nth($style, 2)};
  }
}
  • 全域性引入style/global.scss
// 根目錄下:vue.config.js
module.exports = {
  css: {
    loaderOptions: {
      scss: {
        // @/ 是 src/ 的別名
        // 注意:在 sass-loader v8 中,這個選項名是 "prependData"
        prependData: `@import "@/style/global.scss";`
      },
    }
  }
}
  • style/index.scss定義的程式碼只是不被style中其他css檔案參照到而已,其他的都和global.scss一致
  • 引入style/index.scss
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import './style/index.scss'

createApp(App).use(router).mount('#app')

下面簡單分析和演示下各個style目錄中的程式碼作用。

1.acss

該目錄主要是定義一些簡單的border、color、font-size、margin和padding等程式碼

/* style/acss/border.scss */
@for $i from 1 through 100 {
  [radius#{$i}] { 
    border-radius: #{$i}Px;
    overflow: hidden;
  }
}
[circle] {
  border-radius: 50%;
}

/* style/acss/font-size.scss */
@for $i from 12 through 30 {
  [fz#{$i}] { 
    font-size: #{$i}px;
  }
}

使用acss程式碼

<div class="container">
  <div class="item" radius20>border-radius: 20px;</div>
</div>
<div class="container">
  <div class="item" circle>border-radius: 50%;</div>
</div>
<div class="container">
  <div class="item" fz30>font-size: 30px;</div>
</div>

2.base

該目錄主要是重置專案中一些元素的預設樣式,比如input、hn、p、a等元素

/* style/base/form.scss */
input {
  padding: 0;
  outline: none;
  border: none;
}

/* style/base/link.scss */
a {
  color: #ccc;
  text-decoration: none;
}

3.settings

該目錄是定義全域性的、專案統一規範的文字顏色、邊框顏色等變數

/* style/settings/var.scss */
/* 主題色調 */
$color-primary: #FF5777;
$color-white: #FFFFFF;

/* 文字色調 */
$color-text-primary: green;
$color-text-secondary: #FF4533;
$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-width-base: 1Px !default;
$border-style-base: solid !default;
$border-base: $border-width-base $border-style-base $border-color-base !default;

4.theme

該目錄定義專案各個主題下相關模組的樣式

/* style/theme/default.scss */
[data-theme='default'] .header {
  background: #FF5777;
}
[data-theme='default'] .footer {
  color: #FF5777;
  border: 2px solid #FF5777;;
}

/* style/theme/cool.scss */
[data-theme='cool'] .header {
  background: #409EFF;
}
[data-theme='cool'] .footer {
  color: #409EFF;
  border: 2px solid #409EFF;;
}

我們通過新增html元素上的data-theme屬性和值,即可達到專案主題的變換

<!-- Theme.vue -->
<template>
  <div class="theme">
    <div class="header"></div>
    <div class="theme__set">
      <div class="set set--default" @click="changeTheme('default')"></div>
      <div class="set set--cool" @click="changeTheme('cool')"></div>
    </div>
    <div class="footer"></div>
  </div>
</template>

<script>
export default {
  name: "Theme",
  setup() {
    const changeTheme = (theme = 'default') => {
      window.document.documentElement.setAttribute("data-theme", theme);
    }
    return {
      changeTheme
    }
  }
}
</script>


<!-- Other.vue -->
<template>
  <div class="about">
    <div class="header"></div>
    <div class="about-title">This is an about page title</div>
    <div class="about-content">This is an about page content</div>
    <div class="footer"></div>
  </div>
</template>

5.tools

該目錄是定義一些全域性的公共mixin和function,目前這塊內容比較完善就是SassMagic,感興趣的可以點進來看一下。下面簡單看一下BEM模式的應用

$elementSeparator: '__';
$modifierSeparator: '--';

// 判斷`$selector`中是否包含BEM中Modify
@function containsModifier($selector) {
  $selector: selectorToString($selector);
  @if str-index($selector, $modifierSeparator) {
    @return true;
  } @else {
    @return false;
  }
}

// 將`$selector`轉換成String
@function selectorToString($selector) {
  $selector: inspect($selector); //cast to string
  $selector: str-slice($selector, 2, -2); //remove brackets
  @return $selector;
}

// @param  {String}  $selector
@function getBlock($selector) {
  $selector: selectorToString($selector);
  $modifierStart: str-index($selector, $modifierSeparator) - 1;
  @return str-slice($selector, 0, $modifierStart);
}

@mixin b($block) {
  .#{$block} {
    @content;
  }
}

@mixin e($element) {
  $selector: &;
  @if containsModifier($selector) {
    $block: getBlock($selector);
    @at-root {
      #{$selector} {
        #{$block + $elementSeparator + $element} {
          @content;
        }
      }
    }
  } @else {
    @at-root {
      #{$selector + $elementSeparator + $element} {
        @content;
      }
    }
  }
}

@mixin m($modifier) {
  @at-root {
    #{&}#{$modifierSeparator + $modifier} {
      @content;
    }
  }
}

// @param {string} $block - BEM中的Block
// <div class="block">
//   <div class="block__header">
//     <div class="block__header--css"></div>
//   </div>
// </div>

//  @include b(block) {
//    background: red;
//    @include e(header){
//       font-size: 14px;
//       @include m(css) {
//         font-size: 18px;
//      }
//   };
// }

// 編譯後
// .block {
//   background: red;
// }
// .block__header {
//   font-size: 14px;
// }
// .block__header--css {
//   font-size: 18px;
// }

尾聲

暫時先講這麼多,更多內容可以關注下這個倉庫vue3-css-architecture,會持續更新完善,補充更多的mixin、function,以及在專案中的應用。

(學習視訊分享:)

以上就是淺析CSS中的5種設計模式,聊聊vue專案中CSS目錄程式碼的作用的詳細內容,更多請關注TW511.COM其它相關文章!