關(guān)于
- 今天被人問了一個關(guān)于
react
中的樣式問題无牵,一瞬間腦袋沒反應(yīng)上來好像還回答錯了,有點(diǎn)尷尬水一篇文章記錄一下厂抖。 - 問題描述:在一個
react
父子組件demo
中茎毁,實(shí)際效果與書寫的樣式不太一樣。
問題復(fù)現(xiàn)
直接上代碼描述問題:
Parent.js
import React from 'react';
import Child from './Child'
import './Parent.less'
function Parent() {
return (
<div className="parent">
<Child/>
<div className='component'>parent</div>
</div>
);
}
export default Parent;
Parent.less
.parent{
background-color: blue;
.component{
color: white;
}
}
- Child.js
import React from 'react';
import './Child.less'
function Child() {
return (
<div className="child">
<div className='component'>Child</div>
</div>
);
}
export default Child
Child.less
.child{
background-color: red;
.component{
color: black;
}
}
大概看一下代碼忱辅,是有一個Parent
的父組件七蜘,藍(lán)底白字。還有一個Child
的子組件墙懂,紅底黑字崔梗。
那么實(shí)際渲染出的樣式是什么樣子的呢。如下圖:
實(shí)際看到的效果確實(shí)藍(lán)底白字與紅底白字垒在,為什么與寫的代碼有出入呢蒜魄。
究其原因
-
為什么子組件的字體顏色不是黑色確是白色?
image
打開調(diào)試工具扔亥,看到子組件被渲染成一個<div class="component">Child</div>
但是樣式卻被父組件的樣式給覆蓋變成了白色,
原因:這是因?yàn)樵趙3c 規(guī)范中谈为,CSS 始終是「全局的」旅挤。在傳統(tǒng)的 web 開發(fā)中,最為頭痛的莫過于處理 CSS 問題伞鲫。因?yàn)槿中哉城眩髅鞫x了樣式,但就是不生效秕脓,原因可能是被其他樣式定義所強(qiáng)制覆蓋柒瓣。
- 為什么同樣
.parent .component
和.child .component
是父級覆蓋子級?
這就要涉及到瀏覽器渲染原理與css的瀏覽器解析原則則了
瀏覽器渲染
- 瀏覽器將獲取的HTML文檔解析成DOM樹。
- 處理CSS標(biāo)記吠架,構(gòu)成層疊樣式表模型
CSSOM(CSS Object Model)
芙贫。 - 將
DOM
和CSSOM
合并為渲染樹(rendering tree)
將會被創(chuàng)建,代表一系列將被渲染的對象傍药。 - 渲染樹的每個元素包含的內(nèi)容都是計算過的磺平,它被稱之為
布局layout
。瀏覽器使用一種流式處理的方法拐辽,只需要一次繪制操作就可以布局所有的元素拣挪。 - 將渲染樹的各個節(jié)點(diǎn)繪制到屏幕上,這一步被稱為繪制
painting
俱诸。
需要注意的是菠劝,以上五個步驟并不一定一次性順序完成,比如DOM或CSSOM被修改時睁搭,亦或是哪個過程會重復(fù)執(zhí)行闸英,這樣才能計算出哪些像素需要在屏幕上進(jìn)行重新渲染。而在實(shí)際情況中介袜,JavaScript和CSS的某些操作往往會多次修改DOM或者CSSOM。
css的瀏覽器解析原則
看一個例子:
.nav h3 span {font-size: 16px;}
在我們不知道規(guī)則的情況下出吹,我們是這樣猜測的遇伞,按照常人的思維從左到右。先是找到.nav
,然后向下匹配所有的h3
和span
標(biāo)簽捶牢。如果在向下匹配的過程中鸠珠,沒有匹配上的則回溯到上一級繼續(xù)匹配其他子葉結(jié)點(diǎn)。
但實(shí)際上秋麸,CSS選擇器讀取順序是從右到左
如果是這樣的規(guī)定的話渐排,還是上面的例子就變成了,先找到所有的span
標(biāo)簽灸蟆,然后找span
標(biāo)簽是h3
的驯耻,然后再延著h3
往上尋找,這時候發(fā)現(xiàn)一個選擇器的類名為.nav
就把這個節(jié)點(diǎn)加入結(jié)果集;如果一直往上找直到html
標(biāo)簽都沒找到的話可缚,就放棄這條線霎迫,換到另一個span
進(jìn)行尋找。
那么來看我們的這個Demo
中的結(jié)構(gòu)
<div class="parent">
<div class="child">
<div class="component">Child</div>
</div>
<div class="component">parent</div>
</div>
瀏覽器先找到.component
往上尋找帘靡,發(fā)現(xiàn)了.child .component
這時候渲染出樣式為黑色知给,然后接著向上尋找發(fā)現(xiàn)了.parent .component
發(fā)現(xiàn)存在這個CSS規(guī)則,所以這時候顏色變成了白色
如何變成正確的顏色
問題找到了描姚,是因?yàn)闃邮礁采w了涩赢,那么如何解決這個問題了。這里我們采用了
CSS Modules
方案轩勘。什么是
CSS Modules
?
把CSS
劃分模塊筒扒,自動為類名后面生成一個hash值保證類名全局唯一贬派。CSS Modules
的使用
- 使用
create-react-app
創(chuàng)建項(xiàng)目酒请,修改webpack.config.js
- 在組件中使用
// parent.js
import styles from './Parent.less'
<div className={styles.parent}>
<Child/>
<div className={styles.component}>
css modules parent
</div>
</div>
//child.js
import styles from './Child.less'
<div className={styles.child}>
<div className={styles.component}>
css modules child
</div>
</div>
配置完成之后發(fā)現(xiàn)樣式類名變成了hash值,這樣即保證了類名的唯一不會存在覆蓋的問題
最后
- 文章首發(fā)于:為什么我的樣式不起作用慢哈?
- 參考:瀏覽器渲染原理與過程
- 參考:CSS選擇器從右向左的匹配規(guī)則
- DEMO地址