CSS module 入門

圖片描述
前端發展愈來愈快,這應該是每一個前端開發者的切身感覺,可是CSS 是前端領域中進化最慢的一塊。ES678快速普及前端工程發愈發成熟,CSS 被被遠遠甩在了後面,JS語言模塊已經標準化,CSS仍是在不斷探索,同時這也是一個急需解決的問題。css

要是你之前還從未據說過CSS Modules,那麼這篇就是專門寫給你的。若是你瞭解過它,你就不必再看了,由於他真的很簡單(很強大)。html

CSS的痛點

咱們都知道,CSS入門簡單,深刻就比較難,樣式簡單維護難。CSS痛點也不少前端

一、CSS 的規則是全局的,任何一個組件的樣式規則,都對整個頁面有效。相信寫css的人都會遇到樣式衝突(污染)的問題。webpack

二、爲了解決全局污染的問題,那就把class命名寫長一點吧、加一層父級選擇器、下降衝突的概率,那麼CSS命名混亂了git

三、組件依賴管理不完全,組件應該相互獨立,引入一個組件時,應該只引入它所須要的 CSS 樣式。github

四、代碼難以複用,出現了sass less 之類的工具web

CSS模塊化方案分類

前端發展是飛速的每天有新輪子。天然CSS 模塊化的解決方案有不少,但主要有三類:算法

一、命名約定

規範化CSS的解決方案如:BEM、OOCSS、AMCSS、SMACSS編程

二、CSS in JS

完全拋棄 CSS,用 JavaScript 寫 CSS 規則,styled-components 就是其中表明。sass

三、使用JS 來管理樣式模塊

使用JS編譯原生的CSS文件,使其具有模塊化的能力,表明是 CSS Modules。

可是這些模塊化方案都是各有優缺點,如命名約定:命名複雜、CSS in JS:缺少擴展、 CSS Modules固然也有一些缺點(你得先學會它再去談優劣)。在衆多解決方案中,沒有絕對的優劣。仍是要結合本身的場景來決定。

使用 CSS Modules

CSS Modules不是將CSS改造的具備編程能力,而是加入了局部做用域、依賴管理,這偏偏解決了最大的痛點。能夠有效避免全局污染和樣式衝突,能最大化地結合現有 CSS 生態和 JS 模塊化能力。

啓用 CSS Modules

CSS Modules 很容易學。webpack 自帶的 css-loader 組件,自帶了 CSS Modules,經過簡單的配置便可使用。

// webpack.config.js
const path = require('path')

module.exports = {
  entry: __dirname + '/src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
            }
          }
        ]
       }
    ]
  }
}
// 也可使用下面這種寫法
// loader: "style-loader!css-loader?modules"

如今咱們來寫個Button組件

/* Button.css */
.primary {
    background-color: #1aad19;
    color: #fff;
    border: none;
    border-radius: 5px;
}
// Button.js
import styles from './Button.css';

console.log(styles); // -> {primary: "yTXmm0isaXExoYiZUvKxH"}
const Button = document.createElement('div')
Button.innerHTML = `<button class=${styles.primary}>Submit</button>`

export default Button

// index.js
import Button from './components/Button'

const app = document.getElementById('root')
app.appendChild(Button)

生成HTML爲

<div id="root">
    <div>
        <button class="yTXmm0isaXExoYiZUvKxH">Submit</button>
    </div>
</div>
<!-- yTXmm0isaXExoYiZUvKxH爲CSS Modules自動生成的class類名 -->

CSS Modules 對 CSS 中的 class 名都作了處理,使用對象來保存原 class 和混淆後 class 的對應關係。CSS Modules自動生成的class類名基本就是惟一的,大大下降了項目中樣式覆蓋衝突的概率。

GitHub 示例庫 https://github.com/liuxing/css-modules-demo 經過commit 記錄來查看

定製class類名

css-loader默認的哈希算法是[hash:base64],從前面咱們能夠發現.primary 被編譯成了 yTXmm0isaXExoYiZUvKxH 這樣的字符串。這名字也沒風格了別擔憂,css-loader 爲咱們提供了localIdentName 參數指定生成的名字格式。localIdentName的默認值是[hash:base64]

...

{
    loader: 'css-loader',
    options: {
        modules: true,
        localIdentName: '[name]__[local]--[hash:base64:5]'
    }
}

// 或者
loader: 'style-loader!css-loader?modules&localIdentName=[name]__[local]--[hash:base64:5]'
...

BEM代碼規範再也不是必須的 熟悉BEM的同窗可能發現了上面的命名和BEM有些神似, 雖然它的命名有點奇特,可是 BEM 被很是多的大型項目和團隊採用,這是一種很好的實踐。固然了隨便怎麼寫均可以結合本身的場景來決定,在CSS module中再也不須要遵照BEM規範。

做用域

經過前面的例子能夠感覺到CSS module處理CSS的方式。如今咱們從頭來講做用域。

默認局部做用

CSS不少問題都是由於全局做用域引發的,怎麼樣才能產生局部做用域?經過前面CSS module的例子咱們發現它思路很簡單就是生成惟一的class類名。CSS module將class轉換成對應的全局惟一hash值來造成局部做用域。使用了 CSS Modules 後,就至關於給每一個 class 名外加了一個 :local 這是默認的,也能夠顯式使用

固然,若是你想切換到全局模式,CSS Modules 容許使用:global(.className)的語法,聲明一個全局規則。凡是這樣聲明的class,都不會被編譯成哈希字符串。

/* Button.css */
:global(.btn) {
    color: #fff;
    border: none;
    border-radius: 5px;
}

.primary {
    background-color: #1aad19;
}

/* 
與上面不加`:local`等價 
顯式的局部做用域語法
*/
:local(.warn) {
    background-color: #e64340
}

經過 GitHub 示例庫 https://github.com/liuxing/css-modules-demo 的commit 記錄來查看

CSS Modules下的樣式複用

對於樣式複用,CSS Modules 提供了 composes 組合 的方式來處理。一個選擇器能夠繼承另外一個選擇器的規則

/* Button.css */

.btn {
    /* 全部通用的樣式 */
    color: #fff;
    border: none;
    border-radius: 5px;
    box-sizing: border-box;
}

.primary {
    composes: btn;
    background-color: #1aad19;
}

Button.js

import styles from './Button.css';

console.log(styles);
const Button = document.createElement('div')
Button.innerHTML = `<button class="${styles.primary}">Submit</button>`

export default Button

生成的 HTML 變爲

<div id="root">
    <div>
        <button class="Button__primary--yTXmm Button__btn--nx67B">Submit</button>
    </div>
</div>

咱們發如今 .primary 中 composes 了 .btn,編譯後 .primary 會變成兩個 class。

composes 還能夠也能夠繼承組合其餘CSS文件裏面的規則

/* author.css */

.shadow {
    box-shadow: 0 0 20px rgba(0, 0, 0, .2)
}

Button.css

···
.primary {
    composes: btn;
    composes: shadow from './author.css';
    background-color: #1aad19;
}
···

這是個很強大方便的功能,CSS Modules團隊成員認爲composes是CSS Modules裏最強大的功能:

For me, the most powerful idea in CSS Modules is composition, where you can deconstruct your visual inventory into atomic classes, and assemble them at a module level, without duplicating markup or hindering performance.

一些建議

爲了追求簡單可控,做者建議遵循以下原則:

  • 不使用選擇器,只使用 class 名來定義樣式
  • 不層疊多個 class,只使用一個 class 把全部樣式定義好
  • 全部樣式經過 composes 組合來實現複用
  • 不嵌套

可是建議只是建議,CSS Modules 並不強制你必定要這麼作。怎麼舒服怎麼來

CSS Modules 很好的解決了 CSS 目前面臨的一些痛點以及模塊化難題,同時也支持與 Sass/Less/PostCSS 等搭配使用。

不管是經過遵循的命名標準化的規範,仍是使用本文介紹的CSS Modules,目的都是同樣:可維護的css代碼。具體使用不是有仍是要結合本身的場景來決定。適合的纔是最好的

你們能夠關注個人公衆號,一塊兒玩耍。有技術乾貨也有扯淡亂談,關注回覆[888]領取福利

JavaScript之禪

左手代碼右手磚,拋磚引玉

相關文章
相關標籤/搜索