styled-components 結(jié)合 React 框架使用夺谁,能讓其支持 CSS in JS 的寫法毡琉。比如:
const Button = styled.button`
color: grey;
`;
以上是 SC
(styled-components 簡稱,下同)的一個簡單用法左冬,它很方便的創(chuàng)建了一個 Button 組件葵陵,然后你就可以在任何地方使用這個組件。
<Button>Test</Button>
這一切是怎么做到的呢蘸泻?下面我們一步步拆解琉苇。
語法規(guī)則解釋
有人可能對 SC
的怪異語法規(guī)則有些費(fèi)解,它怎么做到直接在 styled 后面獲得一個 dom 節(jié)點(diǎn)悦施,然后又拼接上一個字符串呢并扇?其實(shí) styled.button 等同于 styled('button'),是一個柯里化后的函數(shù)抡诞,而函數(shù)后可以接模板字符串(兩個反引號包起來部分``)是 ES6 的一個新語法特性穷蛹,相關(guān)文檔,比如我們可以如下使用函數(shù)來接收一個字符串昼汗。
const fun = (args)=>{
console.log(args); // [ 'Hello' ]
}
fun`Hello`
并且其間也可以加入表達(dá)式肴熏。
const fun = (args, exp)=>{
console.log(args); // [ 'Hello ', ', Hi~' ]
console.log(exp); // World
}
const v = "World"
fun`Hello ${v}, Hi~`
可以加入表達(dá)式的特性衍生出了 SC
的一種更高級用法,比如以上的 button顷窒,如果加入一個背景屬性蛙吏,而此屬性會根據(jù) props 而變化,則可以寫成以下樣子鞋吉。
const Button = styled.button`
color: grey;
background: ${props => props.background};
`;
剛剛有人可能好奇 styled.button 是怎么就等同于 styled('button') 的鸦做,其實(shí)也簡單,SC
在倒入 styled 時把所有的 dom 節(jié)點(diǎn)柯里化后的函數(shù)都賦值給了 styled 的同名屬性谓着,這樣就能使用上面的語法方式了泼诱,具體實(shí)現(xiàn)就是下面這段代碼。
domElements.forEach(domElement => {
styled[domElement] = styled(domElement);
});
組件創(chuàng)建過程
那我們在用 SC 的方式聲明了一個 Button 組件后赊锚,SC 做了哪些操作呢治筒?
1,首先生成一個 componentId改抡,SC 會確保這個 id 是唯一的矢炼,大致就是全局 count 遞增、hash阿纤、外加前綴的過程句灌。hash 使用了 MurmurHash,hash 過后的值會被轉(zhuǎn)成字符串欠拾。生成的 id 類似 sc-bdVaJa
componentId = getAlphabeticChar(hash(count))
2胰锌,在 head 中插入一個 style 節(jié)點(diǎn),并返回 className藐窄;創(chuàng)建一個 style 的節(jié)點(diǎn)资昧,然后塞入到 head 標(biāo)簽中,生成一個 className荆忍,并且把模板字符串中的 style 結(jié)合 className 塞入到這個 style 節(jié)點(diǎn)中格带。其中 insertRule 方法
evaluatedStyles 是獲得到的 style 屬性
const style = document.createElement("style");
// WebKit hack
style.appendChild(document.createTextNode(""));
document.head.appendChild(style);
const className = hash(componentId + evaluatedStyles);
style.sheet.insertRule('.className { evaluatedStyles }'撤缴, 0)
3,根據(jù)解析的 props 和 className 來創(chuàng)建這個 element叽唱。
const newElement = React.createElement(
type,
[props],
[...children]
)
newElement.className = "第二部生成的節(jié)點(diǎn)"
結(jié)論
SC
整體原理還是比較簡單的屈呕,由于直接用了 ES6 的的字符模板特性,對 style 和 props 部分的解析也比較快棺亭,由于需要在運(yùn)行過程中不斷動態(tài)創(chuàng)建 Element 并且創(chuàng)建 style 消耗了部分性能虎眨。使用過程中也有些要注意的地方,拿最初的 button 舉例镶摘,如果每次點(diǎn)擊那個 Button 都會改變一次 background嗽桩,你會發(fā)現(xiàn)在點(diǎn) 200 次后,SC
會報一個 warning凄敢。
Over 200 classes were generated for component styled.button.
Consider using the attrs method, together with a style object for frequently changed styles.
Example:
const Component = styled.div.attrs({
style: ({ background }) => ({
background,
}),
})`width: 100%;`
<Component />
warning 表示創(chuàng)建了超過 200 個類碌冶,這是由于每次 background 變化都會生成新的 Element 導(dǎo)致的,此時我們按照提示可以優(yōu)化成直接使用節(jié)點(diǎn)的 style 屬性來修改樣式贡未,避免重復(fù)生成節(jié)點(diǎn)類种樱。
styled.button.attrs({
style: props => ({background: props.background})
})``
css in js 的寫法有很多優(yōu)勢,此處就不多說了俊卤,影響大家使用的很重要一點(diǎn)是性能嫩挤,SC
團(tuán)隊(duì)也一直對外宣稱要不斷改進(jìn)性能,20年的 V5 版本做了很多提升消恍,當(dāng)然由于實(shí)現(xiàn)機(jī)制岂昭,所以再怎么優(yōu)化也不可能達(dá)到原生寫法的性能。如果有對性能要求很嚴(yán)的網(wǎng)站建議不要使用狠怨,如果想寫的快寫的爽约啊,用 SC
還是很不錯的選擇。
號稱是 Beast Mode 的 V5 版本更新宣傳