把問題的結(jié)論放在開頭村生,關(guān)于我的故事惊暴,感興趣的同學(xué)或者覺得會對自己有幫助的同學(xué)慢慢欣賞吧,這樣可以節(jié)省另外部分同學(xué)的時間趁桃。
1. css-modules是什么辽话?
css-modules是將css賦予作用域概念的一種樣式模塊化解決方案。通過給樣式名加hash字符串后綴的方式卫病,實現(xiàn)特定作用域語境中的樣式編譯后的樣式在全局唯一油啤。
2. css-modules解決了什么問題?
當(dāng)項目經(jīng)由多位技術(shù)背景和水平參差不齊的開發(fā)者接手過后蟀苛,項目的樣式已經(jīng)不能乖乖的被后面的同學(xué)搞了益咬,不斷的重構(gòu)重構(gòu)重構(gòu)...... 嗯,是時候要有人拿小皮鞭來規(guī)范開發(fā)秩序了屹逛。css-modules的出現(xiàn)解決了全局樣式污染础废,龐大的頁面dom節(jié)點取名字的問題汛骂。
3. css-modules的科學(xué)實踐
組件容器內(nèi)樣式作用域弱約束,頁面區(qū)域間作用域強(qiáng)隔離评腺。
例如頁面結(jié)構(gòu)為 header帘瞭、main、aside蒿讥、footer 這四個區(qū)域蝶念。header和aside共同使用了Navbar組件,Navbar的組件是復(fù)用了芋绸,樣式一致媒殉,但是在header和aside中要有兩種視覺效果。這時候把header和aside可以分別看做Navbar的兩個實例容器摔敛,header和aside是用css-module管理的local作用域廷蓉,在他們的local作用域中聲明一個global作用域,通過.header :global .nav-bar{...}
的方式覆寫組件樣式马昙,實現(xiàn)同一組件在同一頁面不同區(qū)域的樣式自定義桃犬。
4. 對css-modules出現(xiàn)背景所做的反思
css對于專業(yè)前端來說是很熟悉的,通過命名前綴行楞,結(jié)構(gòu)化命名來描述頁面結(jié)構(gòu)攒暇。但這些都是人為經(jīng)驗去做的代碼管理,在團(tuán)隊開發(fā)資源緊張的情況下子房,很多不熟悉css的后端同學(xué)也需要盡快完成前端的任務(wù)形用,導(dǎo)致大量的樣式?jīng)_突和冗余。于是乎证杭,標(biāo)準(zhǔn)化組件給大家更少的選擇田度,更少的關(guān)注css...
當(dāng)然以上是出于許許多多后端、客戶端發(fā)者對web前端的理解基礎(chǔ)上給出的解決方案躯砰。語言之間的思想是會互相影響的每币,就那這個模塊化的方案結(jié)合es6來說,我感覺就很像python了琢歇。
下面是關(guān)于我對css-modules使用經(jīng)歷的小故事:
不久前換了工作兰怠,開始使用React來開發(fā)web。早先就聽聞過React的大名李茫,之前的工作是團(tuán)隊中就我一只前端揭保,考慮到效率問題,工作中一直在用Angular 1.X 來霸道的進(jìn)行開發(fā)魄宏,功能大而全秸侣,即使是一個人來同時做前后端,也可以很快的開發(fā)出可維護(hù)性的系統(tǒng)。那么現(xiàn)在開始用了React味榛,初次的開發(fā)體驗我選擇了"ant-design"椭坚。
根據(jù)ant-design的快速上手教程,我搭建起來一個初始化的項目結(jié)構(gòu)搏色。但是當(dāng)我需要按照設(shè)計圖去還原視覺效果的時候善茎,我懵逼了。jsx的語法频轿,加上模塊化的React組件垂涯,在開發(fā)界面功能的時候很方便,有種類似客戶端的控件引入方式航邢。不過在我需要按照視覺設(shè)計圖去覆寫組件樣式的時候耕赘,我發(fā)現(xiàn)竟然沒有對應(yīng)的css樣式!
經(jīng)過調(diào)試發(fā)現(xiàn)膳殷,項目結(jié)構(gòu)中引入了css-modules操骡,在瀏覽器的開發(fā)者工具中會看到,頁面中節(jié)點的className被加上了hash字符串赚窃,這也就意味著css有了作用域的概念当娱。后來查css-modules的文檔,如果是全局作用域的樣式需要在樣式前面包一層 :global考榨,代碼如下:
:global {
html, body, #root {
height: 100%;
}
body { background: #fafafa; }
}
在沒有:global包裹的樣式,編譯后的結(jié)果是一下這種:
MainLayout.less
.main{...}
index.html
<div id="root">
<div class="main"></div>
</div>
編譯后的index.html
<div id="root">
<div class="main___1gjAI"></div>
</div>
MainLayout.jsx
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { DatePicker, message } from 'antd';
import styles from './MainLayout.less';
ReactDOM.render(
<div className={styles.main}>
<DatePicker onChange={value => this.handleChange(value)} />
</div>,
document.getElementById('root')
);
這里想要讓css樣式生效鹦倚,必須在對應(yīng)的節(jié)點使用 "module.style"(模
塊.樣式屬性)的方式綁定樣式河质。這里就沒辦法通過外部容器的選擇器來定位組件節(jié)點了。因為組件是在自定義渲染部分引入使用震叙,組件節(jié)點是自動渲染的掀鹅,如果組件沒有開放子節(jié)點的className字段,是無法修改組件樣式的媒楼。
不過乐尊,如果在組件沒有使用css-modules的情況下,我們可以通過在頁面節(jié)點-組件容器的css作用域來創(chuàng)建一個組件容器的全局作用域(該作用域存在于頁面節(jié)點的local作用域內(nèi)划址,頁面其他區(qū)域的css作用域是隔離的)扔嵌,代碼如下:
.main{
:global{
.ant-calendar-picker{
/*這里可以覆寫 DatePicker組件的樣式了*/
}
}
}
在經(jīng)過一陣驚訝和反思后,結(jié)合我之前的工作經(jīng)歷不禁開始贊嘆css-modules的牛逼夺颤。
說他牛逼并不是因為他在技術(shù)上的實現(xiàn)有多么復(fù)雜痢缎,而是他可以讓項目的不確定性大大降低,代碼的可維護(hù)性得以提升世澜。
以前也跟朋友吐槽過独旷,相信前端同學(xué)有的也有過這樣的經(jīng)歷,入職后發(fā)現(xiàn)自己是唯一的前端,而且接手的項目之前是后臺的同學(xué)開發(fā)的嵌洼。素質(zhì)高點的js不會亂引的太嚴(yán)重案疲,然而大多數(shù)后臺的同學(xué)是不熟悉css的,樣式不僅冗余麻养,而且很多因為優(yōu)先級而相互覆蓋的問題褐啡。
換個角度想想就發(fā)現(xiàn)了css-modules的價值,就是tmd讓不熟css的同學(xué)乖乖的不要給人家挖坑回溺。如果說真的需要接手這類的項目春贸,即便是損失了css的靈活性,我也是會開心的笑出眼淚吧(畢竟比反復(fù)重構(gòu)人家寫的頁面要好很多)遗遵。