什么是CSS Modules?
官方解釋是:GitHub - css-modules/css-modules: Documentation about css-modules
可以理解為:所有的類名和動畫名稱默認都有各自的作用域的css文件。
通俗點來說是,每個class類是獨立的可以被單獨的按照某種規則編譯為獨一無二的域名,或者你也可以理解為,每個class都有自己的scope。
css作用域
目前css作用域有三種方案
1.css modules
2.css in js
3.BEM +scope
其中1、3的區別如下
- 只要是靠人去保證代碼質量總是不靠譜的,人的狀態有起伏,但是機器沒有,因此推薦用機器去解決這些問題。這個是BEM+scope比較欠缺的
- 需要注意css穿透,如果要改一些第三方的東西(當然寫起來也比較簡單),具體的可以了解下樣式穿透
- BEM寫起來很繁瑣,而且是靠人去解決重名的問題。
那么cssModule解決了什么問題?
1.變量全局污染問題
css scope也能解決這個問題,但是解決問題的思路不一樣,scope解決全局樣式污染的問題的解決辦法是通過,scope來限制子作用域對父作用域(也可以是全局作用域)的污染
css module解決辦法是通過
:local(.text){ color:red }
:local函數來解決這個問題
:global(.class){ }
:global能在子頁面內寫全局變量(我們在修改第三方庫的時候通常會使用這個函數)
2.對比BEM,BEM相對繁瑣,而CSS Modulex相對靈活
勝出原因:
1.BEM的解決辦法是通過人來保證css的唯一性,CSS Module是通過webpack的打包機制類解決這個問題。
打包代碼之前
<h1 class="test"> An example heading </h1>
打包代碼之后
<h1 class="_styles__test_309571057"> An example heading </h1>
2.靈活,並且不同css文件內即使相同的class也不會互相影響
舉個栗子
import real from './real.css' import fake from './fake.css' element.innerHTML = `<div class="${buttons.red} ${padding.large}">`;
本質上,這種引入兩個文件夾的寫法是完全可以的,但是有些時候這種形式的代碼復用是很實用的,還有一個不經常用的寫法
.element { composes: dark-red from "./colors.css"; font-size: 30px; line-height: 1.2; }
借助CSS Module的compose,來解決這個事情。
那么怎么使用CSS Module?
看了阮一峰大大的CSS Module教程,總結如下:
使項目支持CSS Module
安裝對應的包
TypeScript
,所以可以用
typings-for-css-modules-loader這個包,這個包也可以替代
css-loader
的功能,此外這個包還能根據
.scss
文件里面的類名自動生成對應的
.d.ts
文件:
npm install -D typings-for-css-modules-loader
配置webpack
這個配置接非常簡單了,因為要用typings-for-css-modules-loader
替代css-loader
的功能,所以直接替換即可,將前面sass的配置修改為如下:
配置后在頁面中引入,引入可能會提示找不到該模塊,出現這個問題的原因是:
因為.scss文件中並沒有類似export這樣的關鍵詞用於導出一個模塊,所以也就導致報錯找不到模塊,這個問題需要通過ts的模塊聲明解決。(declare module)
解決聲明模塊問題
在根目錄新建一個typings文件夾,存放.scss的模塊聲明,以及后續需要用到的全局interface等,結構和聲明內容如下:
接下來就可以愉快的寫CSS module了
CSS Module 全局作用域
CSS Modules 允許使用:global(.className)
的語法,聲明一個全局規則。凡是這樣聲明的class
,都不會被編譯成哈希字符串。
.title { color: red; } :global(.title) { color: green; }
App.js
使用普通的class
的寫法,就會引用全局class
。
import React from 'react'; import styles from './App.css'; export default () => { return ( <h1 className="title"> Hello World </h1> ); };
CSS Modules 還提供一種顯式的局部作用域語法:local(.className)
,等同於.className
,所以上面的App.css
也可以寫成下面這樣。
:local(.title) { color: red; } :global(.title) { color: green; }
定制哈希類名
css-loader
默認的哈希算法是[hash:base64]
,這會將.title
編譯成._3zyde4l1yATCOkgn-DBWEL
這樣的字符串。
webpack.config.js
里面可以定制哈希字符串的格式。(此處引用typings-for-css-modules-loader沒有嘗試過定制哈希,你們可以用css-loader的方式嘗試一下)
module: { loaders: [ // ... { test: /\.css$/, loader: "style-loader!css-loader?modules&localIdentName=[path][name]---[local]---[hash:base64:5]" }, ] }
然后.tille 就變成了 (路徑名)_title__2DtM1
Class組合
在CSS Modules中,一個選擇器可以繼承另一個選擇器的規則,這稱為組合(composition)
.className { background-color: blue; } .title { composes: className; color: red; }
輸入變量
CSS Modules 支持使用變量,不過需要安裝 PostCSS 和 postcss-modules-values。
npm install --save postcss-loader postcss-modules-values
把postcss-loader
加入webpack.config.js
。
const values = require('postcss-modules-values'); module.exports = { entry: __dirname + '/index.js', output: { publicPath: '/', filename: './bundle.js' }, module: { loaders: [ { test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel', query: { presets: ['es2015', 'stage-0', 'react'] } }, { test: /\.css$/, loader: "style-loader!css-loader?modules!postcss-loader" }, ] }, postcss: [ values ] };
接着,在colors.css
里面定義變量。
@value blue: #0c77f8; @value red: #ff0000; @value green: #aaf200;
App.css
可以引用這些變量。
@value colors: "./colors.css"; @value blue, red, green from colors; .title { color: red; background-color: blue; }
參考:https://zhuanlan.zhihu.com/p/99748333
參考:http://www.ruanyifeng.com/blog/2016/06/css_modules.html
點贊👍+關注我吧~
我只想成為更好的自己