css工程化解決類名衝突、重複樣式等問題

CSS工程化

在前端的不斷髮展,css也出現了很多問題,類名衝突、重複樣式定義、css文件需要細分等問題。這篇文章我們來依次介紹如何解決這些問題的。

1、命名約定:BEM規範

BEM是一套針對css類樣式的命名方法。

其他命名方法還有:OOCSS、AMCSS、SMACSS等等

BEM全稱是:Block Element Modifier

一個完整的BEM類名:block__element_modifier,例如: banner__dot_selected,可以表示:輪播圖中,處於選中狀態的小圓點

三個部分的具體含義爲:

  • Block:頁面中的大區域,表示最頂級的劃分,例如:輪播圖(banner)、佈局(layout)、文章(article)等等
  • element:區域中的組成部分,例如:輪播圖中的橫幅圖片(banner__img)、輪播圖中的容器(banner__container)、佈局中的頭部(layout__header)、文章中的標題(article_title)
  • modifier:可選。通常表示狀態,例如:處於展開狀態的佈局左邊欄(layout__left_expand)、處於選中狀態的輪播圖小圓點(banner__dot_selected)

在某些大型工程中,如果使用BEM命名法,還可能會增加一個前綴,來表示類名的用途,常見的前綴有:

  • l: layout,表示這個樣式是用於佈局的
  • c: component,表示這個樣式是一個組件,即一個功能區域
  • u: util,表示這個樣式是一個通用的、工具性質的樣式
  • j: javascript,表示這個樣式沒有實際意義,是專門提供給js獲取元素使用的

2、css in js

一看這個名字,我們應該差不多就能纔出來,把css寫在js中。它是把css變成js中的對象, 這樣就可以完全運用js語言的優勢。舉個例子:


const styles = {
    width: "400px",
    height: "500px",
    backgroundColor: "#ccc",
}

css變成對象,就完全不會出現命名衝突的問題。

css in js就有了一下幾個優點:

  • 絕無衝突的可能:由於它根本不存在類名,所以絕不可能出現類名衝突
  • 更加靈活:可以充分利用JS語言靈活的特點,用各種招式來處理樣式
  • 應用面更廣:只要支持js語言,就可以支持css in js,因此,在一些用JS語言開發移動端應用的時候非常好用,因爲移動端應用很有可能並不支持css

但css in js同時也會出現一些問題:

  • 書寫不便:書寫樣式,特別是公共樣式的時候,處理起來不是很方便
  • 在頁面中增加了大量冗餘內容:在頁面中處理css in js時,往往是將樣式加入到元素的style屬性中,會大量增加元素的內聯樣式,並且可能會有大量重複,不易閱讀最終的頁面代碼

那麼如何使用css in js,我們可以通過我們學過的各種技術將其應用到頁面上,比如寫個循環,使用框架等等方式,只要支持js,就可以使用。vue和react都支持css in js

3、css module

見名知意,css module就是把css寫在不同的文件中,最後通過webpack構建工具合併成一個文件。多個不同的文件有相同的類名,合併之後沒有衝突的類名。
在這裏插入圖片描述

在webpack中,我們使用css-loader來處理css文件,它就實現了css module的思想(css-loader使用在webpack常用插件中有講述)。要啓用css module,需要將css-loader的配置modules設置爲true

css module原理非常簡單,css-loader會將樣式中的類名進行轉換,轉換爲一個唯一的hash值。由於hash值是根據模塊路徑和類名生成的,因此,不同的css模塊,哪怕具有相同的類名,轉換後的hash值也不一樣。
在這裏插入圖片描述

因此css-loader使用css module後,源代碼的類名和最終生成的類名是不一樣的,而開發者只知道自己寫的源代碼中的類名,並不知道最終的類名是什麼,css-loader會導出二者的對應關係,但還包括了很多其他信息。而style-loader就是去除其他信息,僅暴露類名和對應生成的hash值

舉個例子:

/*----------index.css----------*/
body{
    background-color: #ccc;
}
h1{
    color: #f40;
}
:global(.yellow){
    color: yellow;
}
.red{
    color: red;
}
/*----------index.js----------*/
import style from "./index.css"
console.log(style)
let h1 = document.getElementsByTagName("h1")[0]
h1.className = style.red
/*----------webpack.config.js----------*/
modules:{
	rules[
       {
		test:/\.css$/,
        use:["style-loader",
             {
            	loader:"css-loader",
            	options:{modules:true}
        	 }
			]
		}
	]
}

解釋

  • 全局類名:某些類名是全局的、靜態的,不需要進行轉換,僅需要在類名位置使用一個特殊的語法即可:
:global(.main){
    ...
}
  • 局部類名:默認就是局部類名local,是可能會造成衝突的類名,會被css module進行轉換

因爲css-loader轉換css代碼後,交給style-loader進習性處理,sytle-loader是用一段js代碼,將樣式加到style文件中。而我們通常更需要的是生成一個css文件。於是就有了庫mini-css-extract-plugin,這個庫提供了一個plugin和一個loader:

  • plugin:負責生成css文件
  • loader:負責記錄要生成的css文件的內容,同時導出開啓css-module後的樣式對象
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
module.exports = {
    module: {
        rules: [
            {
                test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader?modules"]
               
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename:"[name].[hash:5].css"
        }) //負責生成css文件,name是chunk的name
    ]
}

其中"css-loader?modules"等同於

{
    loader:"css-loader",
    options:{
        modules:true
    }
}

4、預處理器

關於預處理器看這篇文章吧!十分鐘帶你學會Less預編譯器

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章