CSS工程化
????

?鏈接每一位開發(fā)者,讓編程更有趣兒!關(guān)注
css的問題
類名沖突的問題
當(dāng)你寫一個(gè)css類的時(shí)候,你是寫全局的類呢?還是寫多個(gè)層級(jí)選擇后的類呢?
你會(huì)發(fā)現(xiàn),怎么都不好!
過深的層級(jí)不利于編寫、閱讀、壓縮、復(fù)用
過淺的層級(jí)容易導(dǎo)致類名沖突
一旦樣式多起來,這個(gè)問題就會(huì)變得越發(fā)嚴(yán)重,其實(shí)歸根結(jié)底,就是類名沖突不好解決的問題。
重復(fù)樣式
這種問題就更普遍了,一些重復(fù)的樣式值總是不斷的出現(xiàn)在css代碼中,維護(hù)起來極其困難。
比如,一個(gè)網(wǎng)站的顏色一般就那么幾種:
- primary
- info
- warn
- error
- success 如果有更多的顏色,都是從這些色調(diào)中自然變化得來,可以想象,這些顏色會(huì)到處充斥到諸如背景、文字、邊框中,一旦要做顏色調(diào)整,是一個(gè)非常大的工程。
css文件細(xì)分問題
在大型項(xiàng)目中,css也需要更細(xì)的拆分,這樣有利于css代碼的維護(hù)。
比如,有一個(gè)做輪播圖的模塊,它不僅需要依賴js功能,還需要依賴css樣式,既然依賴的js功能僅關(guān)心輪播圖,那css樣式也應(yīng)該僅關(guān)心輪播圖,由此類推,不同的功能依賴不同的css樣式、公共樣式可以單獨(dú)抽離,這樣就形成了不同于過去的css文件結(jié)構(gòu):文件更多、拆分的更細(xì)
而同時(shí),在真實(shí)的運(yùn)行環(huán)境下,我們卻希望文件越少越好,這種情況和JS遇到的情況是一致的,因此,對(duì)于css,也需要工程化管理。
從另一個(gè)角度來說,css的工程化會(huì)遇到更多的挑戰(zhàn),因?yàn)閏ss不像JS,它的語法本身經(jīng)過這么多年并沒有發(fā)生多少的變化(css3也僅僅是多了一些屬性而已),對(duì)于css語法本身的改變也是一個(gè)工程化的課題
如何解決
這么多年來,官方一直沒有提出方案來解決上述問題,一些第三方機(jī)構(gòu)針對(duì)不同的問題,提出了自己的解決方案。
解決類名沖突
一些第三方機(jī)構(gòu)提出了一些方案來解決該問題,常見的解決方案如下:
「命名約定」
就是提供一種命名的標(biāo)準(zhǔn),來解決沖突,常見的標(biāo)準(zhǔn)有:
- BEM
- OOCSS
- AMCSS
- SMACSS
- 其他
我主要以BEM為例說下:
BEM是一套針對(duì)css類樣式的命名方法。其他命名方法還有:OOCSS、AMCSS、SMACSS等等
BEM全稱是:Block Element Modifier
一個(gè)完整的BEM類名:block__element_modifier,例如:banner__dot_selected,可以表示:輪播圖中,處于選中狀態(tài)的小圓點(diǎn)

三個(gè)部分的具體含義為:
Block :頁(yè)面中的大區(qū)域,表示最頂級(jí)的劃分,例如:輪播圖(banner)、布局(layout)、文章(article)等等
element :區(qū)域中的組成部分,例如:輪播圖中的橫幅圖片(banner__img)、輪播圖中的容器(banner__container)、布局中的頭部(layout__header)、文章中的標(biāo)題(article__title)
modifier :可選。通常表示狀態(tài),例如:處于展開狀態(tài)的布局左邊欄(layout__left_expand)、處于選中狀態(tài)的輪播圖小圓點(diǎn)(banner__dot_selected) 在某些大型工程中,如果使用BEM命名法,還可能會(huì)增加一個(gè)前綴,來表示類名的用途,常見的前綴有:
l : layout,表示這個(gè)樣式是用于布局的
c : component,表示這個(gè)樣式是一個(gè)組件,即一個(gè)功能區(qū)域
u : util,表示這個(gè)樣式是一個(gè)通用的、工具性質(zhì)的樣式
j : javascript,表示這個(gè)樣式?jīng)]有實(shí)際意義,是專門提供給js獲取元素使用的
「css in js」
這個(gè)方案賊大膽,它覺得,css語言本身幾乎無可救藥了,干脆直接用js對(duì)象來表示樣式,然后把樣式直接應(yīng)用到元素的style中
這樣一來,css變成了一個(gè)一個(gè)的對(duì)象,就可以完全利用到j(luò)s語言的優(yōu)勢(shì),你可以:
- 通過一個(gè)函數(shù)返回一個(gè)樣式對(duì)象
- 把公共的樣式提取到公共模塊中返回
- 應(yīng)用js的各種特性操作對(duì)象,比如:混合、提取、拆分
- 更多的花樣 這種方案在手機(jī)端的React Native中大行其道
css in js 的核心思想是:用一個(gè)JS對(duì)象來描述樣式,而不是css樣式表
例如下面的對(duì)象就是一個(gè)用于描述樣式的對(duì)象:
const?styles?=?{
????backgroundColor:?"#f40",
????color:?"#fff",
????width:?"400px",
????height:?"500px",
????margin:?"0?auto"
}
由于這種描述樣式的方式根本就不存在類名,自然不會(huì)有類名沖突
至于如何把樣式應(yīng)用到界面上,不是它所關(guān)心的事情,你可以用任何技術(shù)、任何框架、任何方式將它應(yīng)用到界面。
css in js 的特點(diǎn):
- 絕無沖突的可能:由于它根本不存在類名,所以絕不可能出現(xiàn)類名沖突
- 更加靈活:可以充分利用JS語言靈活的特點(diǎn),用各種招式來處理樣式
- 應(yīng)用面更廣:只要支持js語言,就可以支持css in js,因此,在一些用JS語言開發(fā)移動(dòng)端應(yīng)用的時(shí)候非常好用,因?yàn)橐苿?dòng)端應(yīng)用很有可能并不支持css
- 書寫不便:書寫樣式,特別是公共樣式的時(shí)候,處理起來不是很方便
- 在頁(yè)面中增加了大量冗余內(nèi)容:在頁(yè)面中處理css in js時(shí),往往是將樣式加入到元素的style屬性中,會(huì)大量增加元素的內(nèi)聯(lián)樣式,并且可能會(huì)有大量重復(fù),不易閱讀最終的頁(yè)面代碼
「css module」
非常有趣和好用的css模塊化方案,編寫簡(jiǎn)單,絕對(duì)不重名
通過命名規(guī)范來限制類名太過死板,而css in js雖然足夠靈活,但是書寫不便。 css module 開辟一種全新的思路來解決類名沖突的問題
思路:
css module 遵循以下思路解決類名沖突問題:
- css的類名沖突往往發(fā)生在大型項(xiàng)目中
- 大型項(xiàng)目往往會(huì)使用構(gòu)建工具(webpack等)搭建工程
- 構(gòu)建工具允許將css樣式切分為更加精細(xì)的模塊
- 同JS的變量一樣,每個(gè)css模塊文件中難以出現(xiàn)沖突的類名,沖突的類名往往發(fā)生在不同的css模塊文件中
- 只需要保證構(gòu)建工具在合并樣式代碼后不會(huì)出現(xiàn)類名沖突即可

實(shí)現(xiàn)原理
在webpack中,作為處理css的css-loader,它實(shí)現(xiàn)了css module的思想,要啟用css module,需要將css-loader的配置modules設(shè)置為true。
css-loader的實(shí)現(xiàn)方式如下:

原理極其簡(jiǎn)單,開啟了css module后,css-loader會(huì)將樣式中的類名進(jìn)行轉(zhuǎn)換,轉(zhuǎn)換為一個(gè)唯一的hash值。
由于hash值是根據(jù)模塊路徑和類名生成的,因此,不同的css模塊,哪怕具有相同的類名,轉(zhuǎn)換后的hash值也不一樣。

如何應(yīng)用樣式:
css module帶來了一個(gè)新的問題:源代碼的類名和最終生成的類名是不一樣的,而開發(fā)者只知道自己寫的源代碼中的類名,并不知道最終的類名是什么,那如何應(yīng)用類名到元素上呢?
為了解決這個(gè)問題,css-loader會(huì)導(dǎo)出原類名和最終類名的對(duì)應(yīng)關(guān)系,該關(guān)系是通過一個(gè)對(duì)象描述的
這樣一來,我們就可以在js代碼中獲取到css模塊導(dǎo)出的結(jié)果,從而應(yīng)用類名了
style-loader為了我們更加方便的應(yīng)用類名,會(huì)去除掉其他信息,僅暴露對(duì)應(yīng)關(guān)系
其他操作
- 全局類名 某些類名是全局的、靜態(tài)的,不需要進(jìn)行轉(zhuǎn)換,僅需要在類名位置使用一個(gè)特殊的語法即可:
:global(.main){
????...
}
使用了global的類名不會(huì)進(jìn)行轉(zhuǎn)換,相反的,沒有使用global的類名,表示默認(rèn)使用了local
:local(.main){
????...
}
使用了local的類名表示局部類名,是可能會(huì)造成沖突的類名,會(huì)被css module進(jìn)行轉(zhuǎn)換
- 如何控制最終的類名 絕大部分情況下,我們都不需要控制最終的類名,因?yàn)榭刂扑鼪]有任何意義
如果一定要控制最終的類名,需要配置css-loader的localIdentName
- 其他注意事項(xiàng)
- css module往往配合構(gòu)建工具使用
- css module僅處理頂級(jí)類名,盡量不要書寫嵌套的類名,也沒有這個(gè)必要
- css module僅處理類名,不處理其他選擇器
- css module還會(huì)處理id選擇器,不過任何時(shí)候都沒有使用id選擇器的理由
- 使用了css module后,只要能做到讓類名望文知意即可,不需要遵守其他任何的命名規(guī)范
解決重復(fù)樣式的問題
「css in js」
這種方案雖然可以利用js語言解決重復(fù)樣式值的問題,但由于太過于激進(jìn),很多習(xí)慣寫css的開發(fā)者編寫起來并不是很適應(yīng)
「css預(yù)編譯器」
有些第三方搞出一套css語言的進(jìn)化版來解決這個(gè)問題,它支持變量、函數(shù)等高級(jí)語法,然后經(jīng)過編譯器將其編譯成為正常的css
這種方案特別像構(gòu)建工具,不過它僅針對(duì)css
常見的預(yù)編譯器支持的語言有:
- less
- sass
基本原理
編寫css時(shí),受限于css語言本身,常常難以處理一些問題:
- 重復(fù)的樣式值:例如常用顏色、常用尺寸
- 重復(fù)的代碼段:例如絕對(duì)定位居中、清除浮動(dòng)
- 重復(fù)的嵌套書寫 由于官方遲遲不對(duì)css語言本身做出改進(jìn),一些第三方機(jī)構(gòu)開始想辦法來解決這些問題,其中一種方案,便是預(yù)編譯器。
預(yù)編譯器的原理很簡(jiǎn)單,即使用一種更加優(yōu)雅的方式來書寫樣式代碼,通過一個(gè)編譯器,將其轉(zhuǎn)換為可被瀏覽器識(shí)別的傳統(tǒng)css代碼。
目前,最流行的預(yù)編譯器有LESS和SASS,它們兩個(gè)特別相似,這里主要說less
- less官網(wǎng):http://lesscss.org/
- less中文文檔1(非官方):http://lesscss.cn/
- less中文文檔2(非官方):https://less.bootcss.com/
- sass官網(wǎng):https://sass-lang.com/
- sass中文文檔1(非官方):https://www.sass.hk/
- sass中文文檔2(非官方):https://sass.bootcss.com/
LESS的安裝和使用
從原理可知,要使用LESS,必須要安裝LESS編譯器
LESS編譯器是基于node開發(fā)的,可以通過npm下載安裝
npm?i?-D?less
安裝好了less之后,它提供了一個(gè)CLI工具lessc,通過該工具即可完成編譯
lessc?less代碼文件?編譯后的文件
光說不練假把式:
新建一個(gè)index.less文件,編寫內(nèi)容如下:
//?less代碼
@red:?#f40;
.redcolor?{
????color:?@red;
}
運(yùn)行命令:
lessc?index.less?index.css
可以看到編譯之后的代碼:
.redcolor?{
??color:?#f40;
}
LESS的基本使用
具體的使用見文檔:https://less.bootcss.com/
- 變量
- 混合
- 嵌套
- 運(yùn)算
- 函數(shù)
- 作用域
- 注釋
- 導(dǎo)入
「postcss」
什么是PostCss?
CSS工程化面臨著諸多問題,而解決這些問題的方案多種多樣。如果把CSS單獨(dú)拎出來看,光是樣式本身,就有很多事情要處理。
既然有這么多事情要處理,何不把這些事情集中到一起統(tǒng)一處理呢?
PostCss就是基于這樣的理念出現(xiàn)的。PostCss類似于一個(gè)編譯器,可以將樣式源碼編譯成最終的CSS代碼
看上去是不是和LESS、SASS一樣呢?
但PostCss和LESS、SASS的思路不同,它其實(shí)只做一些代碼分析之類的事情,將分析的結(jié)果交給插件,具體的代碼轉(zhuǎn)換操作是插件去完成的。

官方的一張圖更能說明postcss的處理流程:

這一點(diǎn)有點(diǎn)像webpack,webpack本身僅做依賴分析、抽象語法樹分析,其他的操作是靠插件和加載器完成的。
- 官網(wǎng)地址:https://postcss.org/
- github地址:https://github.com/postcss/postcss
安裝
PostCss是基于node編寫的,因此可以使用npm安裝
npm?i?-D?postcss
postcss庫(kù)提供了對(duì)應(yīng)的js api用于轉(zhuǎn)換代碼,如果你想使用postcss的一些高級(jí)功能,或者想開發(fā)postcss插件,就要api使用postcss,api的文檔地址是:http://api.postcss.org/
不過絕大部分時(shí)候,我們都是使用者,并不希望使用代碼的方式來使用PostCss
因此,我們可以再安裝一個(gè)postcss-cli,通過命令行來完成編譯
npm?i?-D?postcss-cli
postcss-cli提供一個(gè)命令,它調(diào)用postcss中的api來完成編譯
命令的使用方式為:
postcss?源碼文件?-o?輸出文件
配置文件
和webpack類似,postcss有自己的配置文件,該配置文件會(huì)影響postcss的某些編譯行為。
配置文件的默認(rèn)名稱是:postcss.config.js
例如:
module.exports?=?{
????map:?false,?//關(guān)閉source-map
}
插件
光使用postcss是沒有多少意義的,要讓它真正的發(fā)揮作用,需要插件
postcss的插件市場(chǎng):https://www.postcss.parts/
下面羅列一些postcss的常用插件:
postcss-preset-env
過去使用postcss的時(shí)候,往往會(huì)使用大量的插件,它們各自解決一些問題,這樣導(dǎo)致的結(jié)果是安裝插件、配置插件都特別的繁瑣。
于是出現(xiàn)了這么一個(gè)插件postcss-preset-env,它稱之為postcss預(yù)設(shè)環(huán)境,大意就是它整合了很多的常用插件到一起,并幫你完成了基本的配置,你只需要安裝它一個(gè)插件,就相當(dāng)于安裝了很多插件了。
安裝好該插件后,在postcss配置中加入下面的配置
module.exports?=?{
????plugins:?{
????????"postcss-preset-env":?{}?//?{}?中可以填寫插件的配置
????}
}
這個(gè)插件里面有很多功能,比如說:
- 自動(dòng)廠商前綴
可以實(shí)現(xiàn)某些新的css樣式需要在舊版本瀏覽器中使用廠商前綴
例如:
::placeholder?{
????color:?red;
}
這個(gè)功能在不同的舊版本瀏覽器中需要書寫為
::-moz-placeholder{
????color:?red;
???
}
:-ms-input-placeholder{
????color:?red;
???
}
::placeholder{
????color:?red;
???
}
要完成這件事情,需要使用autoprefixer庫(kù)。而postcss-preset-env內(nèi)部包含了該庫(kù),自動(dòng)有了該功能。
如果需要調(diào)整兼容的瀏覽器范圍,可以通過下面的方式進(jìn)行配置:
【方式1】:添加 .browserslistrc文件
創(chuàng)建文件.browserslistrc,填寫配置內(nèi)容
last?2?version
>?1%
【方式2】:在package.json的配置中加入browserslist
"browserslist":?[
????"last?2?version",
????">?1%"
]
browserslist是一個(gè)多行的(數(shù)組形式的)標(biāo)準(zhǔn)字符串。
它的書寫規(guī)范多而繁瑣,詳情見:https://github.com/browserslist/browserslist
一般情況下,大部分網(wǎng)站都使用下面的格式進(jìn)行書寫
last?2?version
>?1%?in?CN
not?ie?<=?8
- last 2 version: 瀏覽器的兼容最近期的兩個(gè)版本
- 1% in CN: 匹配中國(guó)大于1%的人使用的瀏覽器, in CN可省略
- not ie <= 8: 排除掉版本號(hào)小于等于8的IE瀏覽器 ?默認(rèn)情況下,匹配的結(jié)果求的是并集。
可以通過網(wǎng)站:https://browserl.ist/ ??對(duì)配置結(jié)果覆蓋的瀏覽器進(jìn)行查詢,查詢時(shí),多行之間使用英文逗號(hào)分割。
?browserlist的數(shù)據(jù)來自于CanIUse網(wǎng)站,由于數(shù)據(jù)不是實(shí)時(shí)的,所以不會(huì)特別準(zhǔn)確
- 未來的CSS語法
CSS的某些前沿語法正在制定過程中,沒有形成真正的標(biāo)準(zhǔn),如果希望使用這部分語法,為了瀏覽器兼容性,需要進(jìn)行編譯
過去,完成該語法編譯的是cssnext庫(kù),不過有了postcss-preset-env后,它自動(dòng)包含了該功能。
我們可以通過postcss-preset-env的stage配置,告知postcss-preset-env需要對(duì)哪個(gè)階段的css語法進(jìn)行兼容處理,它的默認(rèn)值為2
"postcss-preset-env":?{
????stage:?0
}
一共有5個(gè)階段可配置:
- Stage 0: Aspirational - 只是一個(gè)早期草案,極其不穩(wěn)定
- Stage 1: Experimental - 仍然極其不穩(wěn)定,但是提議已被W3C公認(rèn)
- Stage 2: Allowable - 雖然還是不穩(wěn)定,但已經(jīng)可以使用了
- Stage 3: Embraced - 比較穩(wěn)定,可能將來會(huì)發(fā)生一些小的變化,它即將成為最終的標(biāo)準(zhǔn)
- Stage 4: Standardized - 所有主流瀏覽器都應(yīng)該支持的W3C標(biāo)準(zhǔn) 了解了以上知識(shí)后,接下來了解一下未來的css語法,盡管某些語法仍處于非常早期的階段,但是有該插件存在,編譯后仍然可以被瀏覽器識(shí)別
① 變量
未來的css語法是天然支持變量的
在:root{}中定義常用變量,使用--前綴命名變量
:root{
????--lightColor:?#ddd;
????--darkColor:?#333;
}
a{
????color:?var(--lightColor);
????background:?var(--darkColor);
}
編譯后,仍然可以看到原語法,因?yàn)槟承┬抡Z法的存在并不會(huì)影響瀏覽器的渲染,盡管瀏覽器可能不認(rèn)識(shí)
如果不希望在結(jié)果中看到新語法,可以配置postcss-preset-env的preserve為false
② 自定義選擇器
@custom-selector?:--heading?h1,?h2,?h3,?h4,?h5,?h6;
@custom-selector?:--enter?:focus,:hover;
a:--enter{
????color:?#f40;
}
:--heading{
????font-weight:bold;
}
:--heading.active{
????font-weight:bold;
}
編譯后
a:focus,a:hover{
????color:?#f40;
}
h1,h2,h3,h4,h5,h6{
????font-weight:bold;
}
h1.active,h2.active,h3.active,h4.active,h5.active,h6.active{
????font-weight:bold;
}
③ 嵌套
與LESS相同,只不過嵌套的選擇器前必須使用符號(hào)&
.a?{
????color:?red;
????&?.b?{
????????color:?green;
????}
????&?>?.b?{
????????color:?blue;
????}
????&:hover?{
????????color:?#000;
????}
}
編譯后
.a?{
????color:?red
}
.a?.b?{
????color:?green;
}
.a>.b?{
????color:?blue;
}
.a:hover?{
????color:?#000;
}
2、postcss-apply
這個(gè)插件可以支持在css中書寫屬性集,類似于LESS中的混入,可以利用CSS的新語法定義一個(gè)CSS代碼片段,然后在需要的時(shí)候應(yīng)用它。
:root?{
??--center:?{
????position:?absolute;
????left:?50%;
????top:?50%;
????transform:?translate(-50%,?-50%);
??};
}
.item{
????@apply?--center;
}
編譯后
.item{
??position:?absolute;
??left:?50%;
??top:?50%;
??-webkit-transform:?translate(-50%,?-50%);
??transform:?translate(-50%,?-50%);
}
?實(shí)際上,該功能也屬于cssnext,不知為何postcss-preset-env沒有支持
3、 postcss-color-function
該插件支持在源碼中使用一些顏色函數(shù)
body?{
????/*?使用顏色#aabbcc,不做任何處理,等同于直接書寫?#aabbcc?*/
????color:?color(#aabbcc);
????/*?將顏色#aabbcc透明度設(shè)置為90%?*/
????color:?color(#aabbcc?a(90%));
????/*?將顏色#aabbcc的紅色部分設(shè)置為90%?*/
????color:?color(#aabbcc?red(90%));
????/*?將顏色#aabbcc調(diào)亮50%(更加趨近于白色),類似于less中的lighten函數(shù)?*/
????color:?color(#aabbcc?tint(50%));
????/*?將顏色#aabbcc調(diào)暗50%(更加趨近于黑色),類似于less中的darken函數(shù)?*/
????color:?color(#aabbcc?shade(50%));
}
編譯后
body?{
????/*?使用顏色#aabbcc,不做任何處理,等同于直接書寫?#aabbcc?*/
????color:?rgb(170,?187,?204);
????/*?將顏色#aabbcc透明度設(shè)置為90%?*/
????color:?rgba(170,?187,?204,?0.9);
????/*?將顏色#aabbcc的紅色部分設(shè)置為90%?*/
????color:?rgb(230,?187,?204);
????/*?將顏色#aabbcc調(diào)亮50%(更加趨近于白色),類似于less中的lighten函數(shù)?*/
????color:?rgb(213,?221,?230);
????/*?將顏色#aabbcc調(diào)暗50%(更加趨近于黑色),類似于less中的darken函數(shù)?*/
????color:?rgb(85,?94,?102);
}
4、stylelint
官網(wǎng):https://stylelint.io/
在實(shí)際的開發(fā)中,我們可能會(huì)錯(cuò)誤的或不規(guī)范的書寫一些css代碼,stylelint插件會(huì)實(shí)時(shí)的發(fā)現(xiàn)錯(cuò)誤。
由于不同的公司可能使用不同的CSS書寫規(guī)范,stylelint為了保持靈活,它本身并沒有提供具體的規(guī)則驗(yàn)證。
你需要安裝或自行編寫規(guī)則驗(yàn)證方案
通常,我們會(huì)安裝tylelint-config-standard庫(kù)提供標(biāo)準(zhǔn)的CSS規(guī)則判定
安裝好后,我們需要告訴stylelint使用該庫(kù)來進(jìn)行規(guī)則驗(yàn)證,告訴的方式有多種,比較常見的是使用文件.stylelintrc
//.styleintrc
{
??"extends":?"stylelint-config-standard"
}
此時(shí),如果你的代碼出現(xiàn)不規(guī)范的地方,編譯時(shí)將會(huì)報(bào)出錯(cuò)誤
body?{
????background:?#f4;
}
發(fā)生了兩處錯(cuò)誤:

- 縮進(jìn)應(yīng)該只有兩個(gè)空格
- 十六進(jìn)制的顏色值不正確
如果某些規(guī)則不是我們所期望的,可以在配置中進(jìn)行設(shè)置:
{
????"extends":?"stylelint-config-standard",
????"rules":?{
????????"indentation":?null
????}
}
設(shè)置為null可以禁用該規(guī)則,或者設(shè)置為4,表示一個(gè)縮進(jìn)有4個(gè)空格。具體的設(shè)置需要參見stylelint文檔:https://stylelint.io/
但是這種錯(cuò)誤報(bào)告需要在編譯時(shí)才會(huì)發(fā)生,如果我希望在編寫代碼時(shí)就自動(dòng)在編輯器里報(bào)錯(cuò)呢?如果想在編輯器里達(dá)到該功能,安裝vscode的插件stylelint就可以了,它會(huì)讀取你工程中的配置文件,按照配置進(jìn)行實(shí)時(shí)報(bào)錯(cuò)。
解決css文件細(xì)分問題
這一部分,就要依靠構(gòu)建工具,例如webpack來解決了,利用一些loader或plugin來打包、合并、壓縮css文件。
「利用webpack拆分css」
要拆分css,就必須把css當(dāng)成像js那樣的模塊;要把css當(dāng)成模塊,就必須有一個(gè)構(gòu)建工具(webpack),它具備合并代碼的能力,而webpack本身只能讀取css文件的內(nèi)容、將其當(dāng)作JS代碼進(jìn)行分析,因此,會(huì)導(dǎo)致錯(cuò)誤。
于是,就必須有一個(gè)loader,能夠?qū)ss代碼轉(zhuǎn)換為js代碼
「css-loader」
css-loader的作用,就是將css代碼轉(zhuǎn)換為js代碼,它的處理原理簡(jiǎn)單到令人發(fā)指:就是將css代碼作為字符串導(dǎo)出。
例如:
.red{
????color:"#f40";
}
經(jīng)過css-loader轉(zhuǎn)換后變成js代碼:
module.exports?=?`.red{
????color:"#f40";
}`
上面的js代碼是經(jīng)過我簡(jiǎn)化后的,不代表真實(shí)的css-loader的轉(zhuǎn)換后代碼,css-loader轉(zhuǎn)換后的代碼會(huì)有些復(fù)雜,同時(shí)會(huì)導(dǎo)出更多的信息,但核心思想不變。
再例如:
.red{
????color:"#f40";
????background:url("./bg.png")
}
經(jīng)過css-loader轉(zhuǎn)換后變成js代碼:
var?import1?=?require("./bg.png");
module.exports?=?`.red{
????color:"#f40";
????background:url("${import1}")
}`;
這樣一來,經(jīng)過webpack的后續(xù)處理,會(huì)把依賴./bg.png添加到模塊列表,然后再將代碼轉(zhuǎn)換為
var?import1?=?__webpack_require__("./src/bg.png");
module.exports?=?`.red{
????color:"#f40";
????background:url("${import1}")
}`;
再例如:
@import?"./reset.css";
.red{
????color:"#f40";
????background:url("./bg.png")
}
會(huì)轉(zhuǎn)換為:
var?import1?=?require("./reset.css");
var?import2?=?require("./bg.png");
module.exports?=?`${import1}
.red{
????color:"#f40";
????background:url("${import2}")
}`;
總結(jié),css-loader干了什么:
- 將css文件的內(nèi)容作為字符串導(dǎo)出
- 將css中的其他依賴作為require導(dǎo)入,以便webpack分析依賴
「style-loader」
由于css-loader僅提供了將css轉(zhuǎn)換為字符串導(dǎo)出的能力,剩余的事情要交給其他loader或plugin來處理。
style-loader可以將css-loader轉(zhuǎn)換后的代碼進(jìn)一步處理,將css-loader導(dǎo)出的字符串加入到頁(yè)面的style元素中
例如:
.red{
????color:"#f40";
}
經(jīng)過css-loader轉(zhuǎn)換后變成js代碼:
module.exports?=?`.red{
????color:"#f40";
}`
經(jīng)過style-loader轉(zhuǎn)換后變成:
module.exports?=?`.red{
????color:"#f40";
}`
var?style?=?module.exports;
var?styleElem?=?document.createElement("style");
styleElem.innerHTML?=?style;
document.head.appendChild(styleElem);
module.exports?=?{}
以上代碼均為簡(jiǎn)化后的代碼,并不代表真實(shí)的代碼,style-loader是有能力避免同一個(gè)樣式的重復(fù)導(dǎo)入
「抽離css文件」
目前,css代碼被css-loader轉(zhuǎn)換后,交給的是style-loader進(jìn)行處理。style-loader使用的方式是用一段js代碼,將樣式加入到style元素中。而實(shí)際的開發(fā)中,我們往往希望依賴的樣式最終形成一個(gè)css文件,此時(shí),就需要用到一個(gè)庫(kù):mini-css-extract-plugin
該庫(kù)提供了1個(gè)plugin和1個(gè)loader
- plugin:負(fù)責(zé)生成css文件
- loader:負(fù)責(zé)記錄要生成的css文件的內(nèi)容,同時(shí)導(dǎo)出開啟css-module后的樣式對(duì)象
使用方式:
const?MiniCssExtractPlugin?=?require("mini-css-extract-plugin")
module.exports?=?{
????module:?{
????????rules:?[
????????????{
????????????????test:?/\.css$/,?use:?[MiniCssExtractPlugin.loader,?"css-loader?modules"]
????????????}
????????]
????},
????plugins:?[
????????new?MiniCssExtractPlugin()?//負(fù)責(zé)生成css文件
????]
}
配置生成的文件名
同output.filename的含義一樣,即根據(jù)chunk生成的樣式文件名。
配置生成的文件名,例如[name].[contenthash:5].css
默認(rèn)情況下,每個(gè)chunk對(duì)應(yīng)一個(gè)css文件。
?? 最后
若本文對(duì)于 CSS工程化 閱讀有任何錯(cuò)誤的地方,歡迎大家給我提意見,一定虛心聽取你們的指正,若覺得不錯(cuò)的,也可以點(diǎn)個(gè)??「star」 ?? 支持一下我。
最后,也可以關(guān)注我的公眾號(hào):「人生不只有技術(shù)」,或是添加我的微信(wKavin)私底下進(jìn)行交流
這篇文章我真的很用心了,你們?nèi)绦牟唤o點(diǎn)個(gè)贊 ?? 和 在看 嘛~
「歡迎各位大佬關(guān)注我,掃二維碼即可」
