為什么要用css modules?
- 代碼只改動一處椰苟;
- 只應用在特定的組件抑月,不影響別的地方;
默認情況下舆蝴,所有類名和動畫名都在本地范圍內(nèi)的css文件谦絮。可以通過webpack或者Browserify來改變類名和選擇器名须误,以保證作用域挨稿。
這可以很好的解決css中的全局作用域問題捶枢。
// AccountInfo.less
.container {
padding: 20px 0 0 40px;
:global {
.ant-col {
font-weight: 700;
text-align: left;
&-5 > label {
margin-left: 7px;
}
}
.ant-form-item {
margin-bottom: 14px;
&-required::before,
&-required::after,
&-label > label::after {
display: none;
}
}
}
}
// 調(diào)用
import styles from './AccountInfo.less';
<div className={styles.container}></div>
編譯后:
<div class="src-biz-AccountManage-AccountInfo-AccountInfo__container--1Fy_N"></div>
composes
關(guān)鍵字
// colors.css
.display {
color: red;
font-size: 30px;
line-height: 35px;
}
// element.css
.element {
composes: display from "./colors.css"
}
composes
關(guān)鍵字指明.normal
包含所有來自.common
的樣式听盖,類似于Sass里的@extends
涂召。但是Sass通過重寫CSS選擇器來實現(xiàn)掸刊,而CSS模塊通過選擇哪個類輸出到JavaScript進行了改變试和。
不再需要BEM
BEM 是 Block蔓榄、Element呵恢、Modifier 的縮寫房午,利用不同的區(qū)塊方淤,功能以及樣式來給元素命名钉赁。這三個部分使用 與 連接(這里用兩個而不是一個是為了留下用于塊的命名)。命名約定如下:
.block {}
.block__element {}
.block--modifier {}
.block__element--modifier {}
BEM 的原則很簡單:一個 Block 代表一個對象(一個人携茂、一個登錄表單你踩、一個菜單);一個 Element 是一個塊中作為特定功能的組件(一個幫助按鈕讳苦、一個登錄按鈕带膜、一個菜單項);一個 Modifier 是我們?nèi)绾伪硎緣K或元素的不同變化(一個女人鸳谜、一個帶有隱藏標簽的迷你登錄框膝藕、 footer 中一個不同的菜單)。
BEM優(yōu)點:
通過這種命名方式咐扭,HTML 層級結(jié)構(gòu)一目了然芭挽,組件功能清晰明朗滑废,而且不必使用過多的層級選擇器,在一定程度上能夠提高 CSS 的渲染速度袜爪。
- 代碼結(jié)構(gòu)更加清晰蠕趁;
- 規(guī)范化
- 利于團隊協(xié)作
BEM缺點:
- 命名過長問。
在 DOM 層級過深的情況下饿敲,會導致 CSS Class 冗長妻导,難以閱讀,所以使用 BEM 命名的層級一般不超過4層怀各。
- 無法根治樣式污染問題
在某些情況下,比如命名稍不注意導致重名术浪,就可能會出現(xiàn)樣式污染問題瓢对,使用第三方庫的情況下也有可能會產(chǎn)生命名沖突,導致樣式污染胰苏。
在構(gòu)建CSS Modules時硕蛹,有兩個好處:
易解析,類似type.display這樣的代碼硕并,對于開發(fā)者來說就像BEM-y的.font-size__serif--large法焰。當BEM選擇器變長時,可能更容易被理解倔毙。
本地作用域埃仪。我們在一個模塊中運用.big中的font-size屬性,也可以在另一個類中陕赃,去同時增加padding和font-size卵蛉。因為本地作用域的不用,他們不會存在沖突么库,甚至可以在一個module中引入2個樣式表傻丝。本地作用域的原因,構(gòu)建工具會給class加上不同的前綴作區(qū)分诉儒。這可以很好的解決css樣式中的特殊性問題葡缰。
styled-components
簡易上手的幾個Demo
const Title = styled.h1`
font-size: 1.5em;
text-align: center;
color: palevioletred;
`;
/* Adapt the colors based on primary prop */
const Button = styled.button`
background: ${props => props.primary ? "palevioletred" : "white"};
color: ${props => props.primary ? "white" : "palevioletred"};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
render(
<div>
<Button>Normal</Button>
<Button primary>Primary</Button>
</div>
)
/* Extending Styles */
const Button = styled.button`
color: palevioletred;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
/* A new component based on Button, but with some override styles */
const TomatoButton = styled(Button)`
color: tomato;
border-color: tomato;
`;
vs 「css in js」的好處
相比于React的 css in js
, styled-components
有一個好處,就是對偽元素的定義忱反。
const Thing = styled.button`
color: blue;
::before {
content: '??';
}
:hover {
color: red;
}
`
css in js
的寫法泛释,偽元素只能通過onMouseOver這樣的js方法去控制,增加代碼量缭受。
Theming in styled-components
styled-components
提供Context這樣的機制胁澳,有一套主題的上下文組件可供使用ThemeProvider
。
import {ThemeProvider} from 'styled-components';
const provider = (
<Provider store={store}>
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
</Provider>
);
ReactDOM.render(
provider,
document.getElementById('root')
);
styled-components
還提供React hook米者,可以結(jié)合useContext 來獲取theme韭畸。
import { useContext } from 'react';
import { ThemeContext } from 'styled-components';
const MyComponent = () => {
const themeContext = useContext(ThemeContext);
console.log('Current theme: ', themeContext);
// ...
}
Issues with specificity
// MyComponent.js
const MyComponent = styled.div`background-color: green;`;
// my-component.css
.red-bg {
background-color: red;
}
// For some reason this component still has a green background,
// even though you're trying to override it with the "red-bg" class!
<MyComponent className="red-bg" />
上述例子中宇智,styled-components
可以取得在全局class之上的優(yōu)先權(quán)。因為styled-components
是在運行時加載到<head>標簽的尾部胰丁。因此它的樣式優(yōu)先級要高于類選擇器随橘。
Tips: 如果想要強行覆蓋組件內(nèi)的樣式,可以通過加大選擇器的權(quán)重來達到覆蓋樣式的效果锦庸。
/* my-component.css */
.red-bg.red-bg {
background-color: red;
}
/* 或者 */
&& {
background-color: red;
}
Tagged Template Literals(標簽模板字符串)
標簽模板字符串机蔗,是ES6的新特性。他們讓你可以自定義字符串差值規(guī)則甘萧,這也是我們可以使用styled-components
的原因萝嘁。
// These are equivalent:
fn`some string here`
fn(['some string here'])
const aVar = 'good'
// These are equivalent:
fn`this is a ${aVar} day`
fn(['this is a ', ' day'], aVar)
本質(zhì)上來說,調(diào)用函數(shù) styled.button() 和使用 styled.button``幾乎是一回事扬卷!但是當你傳入?yún)?shù)時就會看到不同之處了牙言。
我們先創(chuàng)建一個簡單的函數(shù)用于探索:
const logArgs = (...args) => console.log(...args)
const favoriteFood = 'pizza'
logArgs(`I like ${favoriteFood}.`)
// -> I like pizza.
logArgs`I like ${favoriteFood}.`
// -> ["I like ", "."] "pizza"
可以看到,我們不再僅僅是得到了一個內(nèi)容為 "I like pizza" 的字符串怪得。
傳入?yún)?shù)的第一位仍然是數(shù)組咱枉,不過現(xiàn)在有了 2 個元素:
- 位于插值左側(cè)的 I like,作為數(shù)組第一個元素徒恋;
- 位于插值的右側(cè)的 .蚕断,是數(shù)組第二個元素。
插值內(nèi)容 favoriteFoor 成為了第二個傳入?yún)?shù)入挣。
如果我們插入不止一個變量,
const favoriteFood = 'pizza'
const favoriteDrink = 'obi'
logArgs`I like ${favoriteFood} and ${favoriteDrink}.`
// -> ["I like ", " and ", "."] "pizza" "obi"
Why is this useful?
對于 React 組件亿乳,你希望使用 props 值調(diào)整他們的樣式。比如我們通過傳入一個 primary 的 prop 值财岔,讓 <Button /> 組件變大一些风皿,像這樣:
<Button primary />
當你使用 styled-components 傳入一個插值函數(shù),我們其實就向組件傳入了一個 props匠璧,使用它就可以進行組件樣式調(diào)整桐款。
const Button = styled.button`
font-size: ${props => props.primary ? '2em' : '1em'};
`;
現(xiàn)在如果 Button 是個基本按鈕(primary),就有 2em 大小的字體夷恍,否則為 1em魔眨。
// font-size: 2em;
<Button primary />
// font-size: 1em;
<Button />
再看一下logArgs 函數(shù)。我們傳入一個字符串酿雪。
logArgs(`Test ${() => console.log('test')}`)
// -> Test () => console.log('test')
logArgs`Test ${() => console.log('test')}`
// -> ["Test", ""] () => console.log('test')
我們在調(diào)用模版字符串時候遏暴,確實可以拿到函數(shù)了。為了測試指黎,我們來創(chuàng)建一個新的函數(shù)來執(zhí)行所有入?yún)⒌暮瘮?shù):
const execFuncArgs = (...args) => args.forEach(arg => {
if (typeof arg === 'function') {
arg()
}
})
測試調(diào)用一波:
execFuncArgs('a', 'b')
// -> undefined
execFuncArgs(() => { console.log('this is a function') })
// -> "this is a function"
execFuncArgs('a', () => { console.log('another one') })
// -> "another one"
// 用模版字符串測試一波
execFuncArgs`Hi, ${() => { console.log('Executed!') }}`
// -> "Executed!"
execFuncArgs 的第二個參數(shù)其實就是一個函數(shù)朋凉,并且可以執(zhí)行這個函數(shù)。
styled-components的底層就是這么實現(xiàn)的醋安。在渲染時向插值函數(shù)中傳入 props杂彭,我們就可以基于 props 來定制樣式墓毒。
標簽模板字符串使得 styled-components
API 得以實現(xiàn),沒有這個特性 styled-compnents
就不可能出現(xiàn)亲怠。