一直以來 CSS 架構都存在一些問題,比如:
全域汙染
CSS 所有的樣式都是全域,因此可能會發生命名空間衝突,樣式互相覆蓋的問題。有時候為了解決問題,而使用了非常醜的 !important
命名混亂
也由於全域汙染的原因,我們會使用一些命名風格規範,像是 BEM
,但在多人協做開發時,控管不易,容易造成命名混亂,選擇器越來越複雜的情況。
架構問題
一般專案大多是以 頁面
為單位來架構 CSS,然後把一些功能模組 ( 比如 Form
Mixin
Variables
) 等拆分出來,讓其他頁面去共用。這樣以 頁面
為基礎的架構,與 React、Vue 等當前熱門的前端框架以 組件
為基礎的架構似乎格格不入。
CSS Modules
跟我們專案所使用的 React 設計理念 萬物皆組件
更加契合。並且透過下面的機制,有效解決了上述的問題。
:local
,有效解決了全域汙染及命名混亂的問題,同時也可以透過 :global
來定義全域樣式。npm i css-loader -D
將 css-loader modules
設置為 true 來開啟 CSS Modules 功能,同時設置 localIdentName
客製化 className 產生的規則。
{
test: /\.(css|scss)$/,
use: [
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[name]_[local]-[hash:base64:5]'
}
]
}
我們新增一個 Component Header
來實測 CSS Modules 功能
┝╸ Components
┝╸ Header
┝╸ Header.js
┕╸ Header.scss
在 Header.scss
定義兩個樣式 .header
.logo
.header {
background-color: #999;
margin: 5px 0;
}
.logo {
display: inline-block;
height: 50px;
padding: 12px 30px;
text-align: center;
background-color: #666;
color: white;
font-size: 18px;
}
在 Header.js
引入 Header.scss
,並且在 Header 裡面引用 styles 裡透過 CSS Modules 處理過的 className。
import styles from './Header.scss'
const Header = props => {
return (
<div className={styles.header}>
<div className={styles.logo}>ASP.NET MVC with Webpack</div>
</div>
)
}
export default Header
import 進來的 styles
物件是什麼呢?其實就是原本定義的 className 對應 CSS Modules 處理過的 className
也就是說 component 裡面去引用 styles.logo 這個樣式
<div className={styles.logo}>ASP.NET MVC with Webpack</div>
實際產生在頁面上的 className 是這樣
<div class="Header_logo-2eVyK">ASP.NET MVC with Webpack</div>
透過 CSS Modules 幫我們產生獨一無二的 className,我們無須再擔心命名空間互相覆蓋的問題,實際的 UI 畫面如下
透過 CSS Modules 處理過的 className