摘要:預處理器最大的好處就是可以支持模塊引入,用的方式來編寫,解決了部分混亂以及代碼冗余的問題,但是也不能完全避免。
或者可以這么說,CSS Modules為我們解決了什么痛點。針對以往我寫網頁樣式的經驗,具體來說可以歸納為以下幾點:
過程是這樣的:你現在有兩個模塊,分別為A、B,你可能會多帶帶針對這兩個模塊編寫自己的樣式,例如a.css、b.css,看一下代碼
// A.js
import "./a.css"
const html = "module A
"
// B.js
import "./b.css"
const html = "module B
"
下面是樣式:
/* a.css */
.text {
color: red;
}
/* b.css */
.text {
color: blue;
}
導入到入口APP中
// App.js
import A from "./A.js"
import B from "./B.js"
element.innerTHML = "xxx"
由于樣式是統一加載到入口中,因此實際上的樣式合在一起(這里暫定為mix.css)顯示順序為:
/* mix.css */
/* a.css */
.text {
color: red;
}
/* b.css */
.text {
color: blue;
}
根據CSS的Layout規則,因此后面的樣式會覆蓋掉前面的樣式聲明,最終有效的就是text
的顏色為blue
的那條規則,這就是全局樣式覆蓋,同理,這在js
中也同樣存在,因此就引入了模塊化,在js中可以用立即執行函數表達式來隔離出不同的模塊
var moduleA = (function(document, undefined){
// your module code
})(document)
var moduleB = (function(document, undefined){
// your module code
})(document)
而在css中要想引入模塊化,那么就只能通過namespace
來實現,而這個又會帶來新的問題,這個下面會講到
為了解決全局樣式的沖突問題,就不得不引入一些特地命名namespace
來區分scope
,但是往往有些namespace
命名得不夠清晰,就會造成要想下一個樣式不會覆蓋,就要再加一個新的namespace
來進行區分,最終可能一個元素最終的顯示樣式類似如以下:
.widget .table .row .cell .content .header .title {
padding: 10px 20px;
font-weight: bold;
font-size: 2rem;
}
在上一個元素的顯示上使用了7個選擇器,總結起來會有以下問題:
content
、title
以及item
這些通用的類名時,你可能要花上老半天才知道它們到底是用在哪個元素上【注】CSS的渲染規則可以參看這篇文章探究 CSS 解析原理
由于CSS不能使用類似于js的模塊化的功能,可能你在一個css文件中寫了一個公共的樣式類,而你在另外一個css也需要這樣一個樣式,這時候,你可能會多寫一次,類似于這樣的
/* a.css */
.modal {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1;
background-color: rgba(0, 0, 0, 0.7);
}
.text {
color: red;
}
/* b.css */
.modal {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1;
background-color: rgba(0, 0, 0, 0.7);
}
.text {
color: blue;
}
那么在合并成app.css的時候,就會被編寫兩遍,雖然樣式不會被影響,但是這樣實際上也是一種字節浪費,當然,上述的這種情況完全是可以通過公用全局樣式來達到目的,但是,這種代碼重復通常是在不知情的情況下發生的。
針對上述的一些問題,也有一些解決方案,具體如下:
Sass,Less的用法這里不再贅述,如果不清楚,可以自己查閱相關資料去了解一下。
CSS預處理器最大的好處就是可以支持模塊引入,用js的方式來編寫CSS,解決了部分scope
混亂以及代碼冗余的問題,但是也不能完全避免。同時,也沒有解決全局樣式的沖突問題
一個SASS
的的文件是這樣的:
/* app.sass */
@import "./reset"
@import "./color"
@import "./font"
可以實際上編譯之后,終究還是一個文件,因此不可避免的會出現沖突樣式
There are only two hard problems in Computer Science: cache invalidation and naming things — Phil Karlton
BEM
就是為了解決命名沖突以及更好的語義化而生的。
Block:邏輯和頁面功能都獨立的頁面組件,是一個可復用單元,特點如下:
[可選]定義Block和Element的外觀及行為,就像HTML屬性一樣,能讓同一種Block看起來不一樣
Block
作為最小的可復用單元,任意嵌套不會影響功能和外觀,命名可以為header
、menu
等等
...
Element
依附Block存在,沒有多帶帶的含義,命名上語義盡量接近于Block,比如title
、item
之類
Header
Modifier
是一個元素的狀態顯示,例如active
、current
、selected
Header
【說明】
Block__Element-father__Element-son_Modifer
這種類名的寫法,BEM只有三級
.form { }
.form--theme-xmas { }
.form--simple { }
.form__input { }
.form__submit { }
.form__submit--disabled { }
參考鏈接:
BEM解決了模塊復用、全局命名沖突等問題,配合預處理CSS使用時,也能得到一定程度的擴展,但是它依然有它的問題:
說了這么多,終于要到正文了
根據CSS Modules的repo上的話來說是這樣的:
CSS files in which all class names and animation names are scoped locally by default.
所以CSS Modules并不是一個正式的聲明或者是瀏覽器的一個實現,而是通過構建工具(webpack or Browserify)來使所有的class達到scope的一個過程。
/* App.css */
.text {
color: red;
}
/* 編譯之后可能是這樣的 */
.App__text___3lRY_ {
color: red;
}
命名唯一,因此保證了全局不會沖突。
可以使用composes
來引入自身模塊中的樣式以及另一個模塊的樣式:
.serif-font {
font-family: Georgia, serif;
}
.display {
composes: serif-font;
font-size: 30px;
line-height: 35px;
}
應用到元素上可以這樣使用:
import type from "./type.css";
element.innerHTML =
`
This is a heading
`;
之后編譯出來的模板可能是這樣的:
Heading title
從另一個模塊中引入,可以這樣寫:
.element {
composes: dark-red from "./colors.css";
font-size: 30px;
line-height: 1.2;
}
因為CSS Modules只關注與組件本身,組件本身基本都可以使用扁平的類名來寫,類似于這樣的:
.root {
composes: box from "shared/styles/layout.css";
border-style: dotted;
border-color: green;
}
.text {
composes: heading from "shared/styles/typography.css";
font-weight: 200;
color: green;
}
CSS Modules不局限于你使用哪個前端庫,無論是React、Vue還是Angular,只要你能使用構建工具進行編譯打包就可以使用。
下面我使用webpack
為例,一步一步引入CSS Modules.
.
├── build
│?? └── bundle.js
├── index.html
├── node_modules
├── package-lock.json
├── package.json
├── src
│?? ├── index.js
│?? └── styles
└── webpack.config.js
index.js作為程序入口,styles文件夾存放樣式文件,配合webpack.config.js作為webpack配置文件。
// index.js
var html = `
CSS Modules
`
document.getElementById("container").innerHTML = html;
樣式文件:
/* global.css */
* {
margin: 0;
padding: 0;
}
.container {
padding: 20px;
}
/* index.css */
.header {
font-size: 32px;
}
.title {
border-bottom: 1px solid #ccc;
padding-bottom: 20px;
}
模板文件:
css modules
全局安裝依賴,配置執行腳本:
npm install webpack webpack-cli --save-dev
package.json
"scripts": {
"build": "npx webpack && open index.html"
}
在控制臺執行npm run build
, 得到的結果為:
> css-modules-demo@1.0.0 build /Users/yhhu/Documents/coding/css-modules-demo
> npx webpack && open index.html
Hash: 5810d2ecd760c08cc078
Version: webpack 4.17.1
Time: 78ms
Built at: 2018-08-26 15:09:31
Asset Size Chunks Chunk Names
bundle.js 3.97 KiB main [emitted] main
Entrypoint main = bundle.js
[./src/index.js] 196 bytes {main} [built]
package.json中加入能夠處理css的loader
module: {
rules: [
{
test: /.js/,
loader: "babel-loader",
include: __dirname + "/src",
exclude: __dirname + "/src/styles"
},
{
test: /.css$/,
use: [
{ loader: "style-loader" },
{
loader: "css-loader",
options: {
}
}
]
}
]
}
index.js中引入兩個CSS文件
// index.js
import "./styles/global.css"
import "./styles/index.css"
const html = `
CSS Modules
`
document.getElementById("container").innerHTML = html;
編譯之后的執行結果為:
在瀏覽器中顯示為:
可以看到打包之后的build
目錄下只有一個bundle.js
,我們現在要把樣式文件提取出來
./build/
└── bundle.js
npm install --save-dev mini-css-extract-plugin
webpack.config.js
var MiniCssExtractPlugin = require("mini-css-extract-plugin");
modules: {
rules: [
// {
// test: /.css$/,
// use: [
// { loader: "style-loader" },
// {
// loader: "css-loader",
// options: {
// }
// }
// ]
// },
{
test: /.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: "./build/styles"
}
},
{
loader: "css-loader",
options: {
}
}
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
})
],
可以看到有main.css
生成
默認在css-loader
中是不開啟css modules
功能的,要開啟可以設置modules: true
即可,更多可以參看官方css-loader
使用方法修改webpack.config.js
,如下:
{
test: /.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: "./build/styles"
}
},
{
loader: "css-loader",
options: {
modules: true
}
}
]
}
修改index.js
文件中的引用方式:
import "./styles/global.css"
import Index from "./styles/index.css"
const html = `
CSS Modules
`
document.getElementById("container").innerHTML = html;
可以看到,之前都是直接import
一個css
文件,而現在改成了導出一個對象的形式,我們可以把Index
對象打印出來,看看具體是些什么東西:
直接對應我們引用的方式,然后我們再看看生成出來的main.css
中具體有哪些東西:
* {
margin: 0;
padding: 0;
}
._2BQ9qrIFipNbLIGEytIz5Q {
padding: 20px;
}
._3Ukt9LHwDhphmidalfey-S {
font-size: 32px;
}
._3XpLkKvmw0hNfJyl8yU3i4 {
border-bottom: 1px solid #ccc;
padding-bottom: 20px;
}
合成一個文件之后,所有的類名都經過了哈希轉換,因此確保了類名的唯一性,我們再看看瀏覽器中inspector
中的樣式應用,如下:
事實上,container
樣式我們是不需要轉換的,因為我是把它固定寫死在了容器上,那我們應該怎么做呢?
要想一個類名不需要被裝換,那么可以使用:global(className)
來進行包裝,這樣的類不會被轉換,會被原樣輸出,下面我們修改global.css
/* global.css */
* {
margin: 0;
padding: 0;
}
:global(.container) {
padding: 20px;
}
我們再來看看main.css
就可以發現.container
類沒有被轉換
CSS Modules默認是以[hash:base64]來進行類名轉換的,可辨識度不高,因此我們需要自定義
開啟自定義,可以使用一個配置參數localIdentName
,具體配置如下:
{
loader: "css-loader",
options: {
modules: true,
localIdentName: "[path][name]__[local]--[hash:base64:5]"
}
}
如果我們實現類似于Sass
的繼承功能,我們需要怎么做呢?CSS Modules中提供了composes
關鍵字讓我們來繼承另外一個類,修改index.css
如下:
.red {
color: red;
}
.header {
font-size: 32px;
}
.title {
composes: red;
border-bottom: 1px solid #ccc;
padding-bottom: 20px;
}
我們增加了一個red
的類名,在title
中實現繼承,編譯之后的結果為:
發現多了一個src-styles-index__red--1ihPk
的類名,正是我們上面繼承的那個類
除了在自身模塊中繼承,我們還可以繼承其他文件中的CSS規則,具體如下:
我們再styles
文件夾下新建一個color.css
/* color.css */
.red {
color: red;
}
.blue {
color: blue;
}
然后在index.css
文件中導入
/* index.css */
.red {
color: red;
}
.header {
font-size: 32px;
}
.title {
color: green;
composes: blue from "./color.css";
composes: red;
border-bottom: 1px solid #ccc;
padding-bottom: 20px;
}
最終我們會發現文字的顏色為綠色,可見自身模塊聲明優先級最高,如果把自身申明的color
去掉,那么自身引入和從其他文件引入的相同申明又該如何顯示呢?
答案是自身引入的聲明的優先級會比較高。
至此,所有的CSS Modules用法就已經介紹完畢了,至于后續的還有如何應用于React
、Vue
以及Angular
中,相信掌握了上面的內容之后就可以知道怎么寫了,如何與預處理器一起使用相信問題也不大。
最后,本文代碼倉庫庫為:https://github.com/Rynxiao/css-modules-demo
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/1878.html
摘要:示例庫通過記錄來查看定制類名默認的哈希算法是,從前面我們可以發現被編譯成了這樣的字符串。與上面不加等價顯式的局部作用域語法通過示例庫的記錄來查看下的樣式復用對于樣式復用,提供了組合的方式來處理。 showImg(https://segmentfault.com/img/bV9WfX?w=800&h=274);前端發展越來越快,這應該是每個前端開發者的切身感受,但是CSS 是前端領域中進...
摘要:前端日報精選精讀引擎特性帶來的的性能變化中的藝術譯你是如何拆分組件的高級技巧如何遍歷中對象屬性一個可視化并且能快速生成模擬數據的持久化服務中文第期中的執行上下文和調用棧是什么譯誰更適合前端開發掘金實戰桌面計算器應用我們日夜期盼的 2017-09-03 前端日報 精選 精讀《V8 引擎特性帶來的的 JS 性能變化》CSS中的藝術[譯] 你是如何拆分組件的?JS高級技巧如何遍歷JavaSc...
摘要:能最大化地結合現有生態預處理器后處理器等和模塊化能力,幾乎零學習成本。編碼相關的所有樣式上例中打印的結果是注意到是按照自動生成的名。實踐手動引用渲染結果使用可以實現使用屬性自動加載模塊。 文章同步于Github Pines-Cheng/blog 隨著前端這幾年的風生水起,CSS作為前端的三劍客之一,各種技術方案也是層出不窮。從CSS prepocessor(SASS、LESS、Styl...
摘要:通過這個教程學習如何使用打包工具配合來取代或處理樣式文件。使用這個命令安裝插件更新。如果你沒有項目的副本,你可以通過這條命令克隆在結束這個狀態下的項目為添加監聽插件。在代碼塊內,添加如下內容簡單起見我省略了文件的大部分內容 通過這個教程學習如何使用JavaScript打包工具Rollup配合PostCSS來取代Grunt或Gulp處理樣式文件。 上一篇文章中,我們完成了使用Rollup...
摘要:之前在簡書上看到一個入門入門,看這篇就夠了,講得確實很清楚,但是因為博主用的是的版本,和現在普遍默認安裝的版本有一些細節上的差距,所以實際使用的時候就會遇到一些坑,對于想入門的小白如我,造成了不小的困擾。 之前在簡書上看到一個webpack入門(入門Webpack,看這篇就夠了),講得確實很清楚,但是因為博主用的是1.x的版本,和現在普遍默認安裝的2.x版本有一些細節上的差距,所以實際...
閱讀 2958·2021-11-08 13:20
閱讀 1031·2021-09-22 15:20
閱讀 660·2019-08-30 15:53
閱讀 1964·2019-08-30 15:43
閱讀 1278·2019-08-29 17:21
閱讀 540·2019-08-29 12:15
閱讀 2375·2019-08-28 17:51
閱讀 3142·2019-08-26 13:26