- 目前整個前端已經(jīng)是組件化的天下:
- 而CSS的設(shè)計就不是為組件化而生的,所以在目前組件化的框架中都在需要一種合適的CSS解決方案仅仆。
- 在組件化中選擇合適的CSS解決方案應(yīng)該符合以下條件:
- 可以編寫局部css:css具備自己的局部作用域顿锰,不會隨意污染其他組件內(nèi)的元素谨垃;
- 可以編寫動態(tài)的css:可以獲取當(dāng)前組件的一些狀態(tài),根據(jù)狀態(tài)的變化生成不同的css樣式硼控;
- 支持所有的css特性:偽類刘陶、動畫、媒體查詢等牢撼;
- 編寫起來簡潔方便匙隔、最好符合一貫的css風(fēng)格特點(diǎn);
- 等等...
- 事實上浪默,css一直是React的痛點(diǎn)牡直,也是被很多開發(fā)者吐槽缀匕、詬病的一個點(diǎn)。
- 在這一點(diǎn)上碰逸,Vue做的要確實要好于React:
- Vue通過在.vue文件中編寫
<style><style>
標(biāo)簽來編寫自己的樣式乡小; - 通過是否添加 scoped 屬性來決定編寫的樣式是全局有效還是局部有效;
- 通過 lang 屬性來設(shè)置你喜歡的 less饵史、sass等預(yù)處理器满钟;
- 通過內(nèi)聯(lián)樣式風(fēng)格的方式來根據(jù)最新狀態(tài)設(shè)置和改變css;
- 等等...
- Vue在CSS上雖然不能稱之為完美胳喷,但是已經(jīng)足夠簡潔湃番、自然、方便了吭露,至少統(tǒng)一的樣式風(fēng)格不會出現(xiàn)多個開發(fā)人員吠撮、多個項目
采用不一樣的樣式風(fēng)格。 - 相比而言讲竿,React官方并沒有給出在React中統(tǒng)一的樣式風(fēng)格:
- 由此泥兰,從普通的css,到css modules题禀,再到css in js鞋诗,有幾十種不同的解決方案,上百個不同的庫迈嘹;
- 大家一致在尋找最好的或者說最適合自己的CSS方案削彬,但是到目前為止也沒有統(tǒng)一的方案;
內(nèi)聯(lián)樣式
- 內(nèi)聯(lián)樣式是官方推薦的一種css樣式的寫法:
- style 接受一個采用小駝峰(原生中使用的是連接符)命名屬性的 JavaScript 對象
<h2 style={{fontSize: "50px", color: "red"}}>我是標(biāo)題</h2>
秀仲,而不是 CSS 字符串融痛; - 并且可以引用state中的狀態(tài)來設(shè)置相關(guān)的樣式;
- style 中屬性值需要用雙引號或單引號引起來, 不然會被認(rèn)為是變量
constructor(props) {
super(props);
this.state = {
color: "purple"
}
}
render() {
const pStyle = {
color: this.state.color,
textDecoration: "underline"
}
return (
<div>
<h2 style={{fontSize: "50px", color: "red"}}>我是標(biāo)題</h2>
<p style={pStyle}>我是一段文字描述</p>
</div>
)
}
- 內(nèi)聯(lián)樣式的優(yōu)點(diǎn):
- 1.內(nèi)聯(lián)樣式, 樣式之間不會有沖突
- 2.可以動態(tài)獲取當(dāng)前state中的狀態(tài)
- 內(nèi)聯(lián)樣式的缺點(diǎn):
- 1.寫法上都需要使用駝峰標(biāo)識
- 2.某些樣式?jīng)]有提示,比如在標(biāo)簽內(nèi)寫 style 屬性時有提示,但是如果像上面
pStyle
拿到外面寫就不會有提示了 - 3.大量的樣式, 代碼混亂
- 4.某些樣式無法編寫(比如偽類/偽元素)
- 所以官方依然是希望內(nèi)聯(lián)合適和普通的css來結(jié)合編寫神僵;
普通的 CSS
- 普通的css我們通常會編寫到一個單獨(dú)的文件酌心,之后再進(jìn)行引入。
- 這樣的編寫方式和普通的網(wǎng)頁開發(fā)中編寫方式是一致的:
- 如果我們按照普通的網(wǎng)頁標(biāo)準(zhǔn)去編寫挑豌,那么也不會有太大的問題安券;
- 但是組件化開發(fā)中我們總是希望組件是一個獨(dú)立的模塊,即便是樣式也只是在自己內(nèi)部生效氓英,不會相互影響侯勉;
- 但是普通的css都屬于全局的css,樣式之間會相互影響铝阐;
- 這種編寫方式最大的問題是樣式之間會相互層疊掉;
比如我們在 home 組件中有個 css 文件,寫了個title 選擇器的樣式
.title {
font-size: 30px;
color: red;
}
然后在 profile 組件中有個 css 文件,也寫了個 title 選擇器的樣式
.title {
color: yellow;
}
由于 css 是全局的, 所以這兩個 title 選擇器就會發(fā)生層疊,也就是在使用到 title 選擇器的地方要么都顯示紅色,要么都顯示黃色, 不會是我們希望的 home 中顯示紅色,profile 中顯示黃色
css modules
- css modules并不是React特有的解決方案址貌,而是所有使用了類似于webpack配置的環(huán)境下都可以使用的。
- 但是,如果在其他項目中使用個练对,那么我們需要自己來進(jìn)行配置遍蟋,比如配置webpack.config.js中的modules: true等。
- React的腳手架已經(jīng)內(nèi)置了css modules的配置:
- .css/.less/.scss 等樣式文件都修改成.module.css/.module.less/.module.scss 等螟凭;
- 之后就可以引用并且進(jìn)行使用了虚青;
比如我們定義了一個 style.module.css 文件,文件內(nèi)容如下:
.title {
font-size: 30px;
color: red;
}
.banner {
color: orange;
}
在 index.js 文件中的使用如下:
import React, { PureComponent } from 'react';
import homeStyle from './style.module.css'; // 這里我們按包導(dǎo)入的方式導(dǎo)入,在使用的時候都是用homeStyle.xxx 的方式使用
export default class Home extends PureComponent {
render() {
return (
<div className="home">
<h2 className={homeStyle.title}>標(biāo)題</h2>
<div className={homeStyle.banner}>
<span>輪播圖</span>
</div>
</div>
)
}
}
- css modules確實解決了局部作用域的問題,也是很多人喜歡在React中使用的一種方案螺男。
- 但是這種方案也有自己的缺陷:
- 引用的類名棒厘,不能使用連接符(.home-title),在JavaScript中是不識別的下隧;
- 所有的className都必須使用{style.className} 的形式來編寫奢人;
- 不方便動態(tài)來修改某些樣式,依然需要使用內(nèi)聯(lián)樣式的方式淆院;
- 如果你覺得上面的缺陷還算OK何乎,那么你在開發(fā)中完全可以選擇使用css modules來編寫,并且也是在React中很受歡迎的一種方式土辩。
CSS in JS
- CSS-in-JS是指一種模式宪赶,其中 CSS 由 JavaScript 生成而不是在外部文件中定義。注意此功能并不是 React 的一部分脯燃,而是由第三方庫提供。 React 對樣式如何定義并沒有明確態(tài)度蒙保。
- 在傳統(tǒng)的前端開發(fā)中辕棚,我們通常會將結(jié)構(gòu)(HTML)、樣式(CSS)邓厕、邏輯(JavaScript)進(jìn)行分離逝嚎。
- React的思想中認(rèn)為邏輯本身和UI是無法分離的,所以才會有了JSX的語法详恼。
- 樣式也是屬于UI的一部分补君;
- 事實上CSS-in-JS的模式就是一種將樣式(CSS)也寫入到JavaScript中的方式,并且可以方便的使用JavaScript的狀態(tài)昧互;
- 所以React又被人稱之為 All in JS挽铁;
- 當(dāng)然,這種開發(fā)的方式也受到了很多的批評:
- 有一篇叫做 Stop using CSS in JavaScript for web development 專門
寫了 css-in-js 不好的地方
- 批評聲音雖然有敞掘,但是在我們看來很多優(yōu)秀的CSS-in-JS的庫依然非常強(qiáng)大叽掘、方便:
- CSS-in-JS通過JavaScript來為CSS賦予一些能力,包括類似于CSS預(yù)處理器一樣的樣式嵌套玖雁、函數(shù)定義更扁、邏輯復(fù)用、動態(tài)修
改狀態(tài)等等; - 雖然CSS預(yù)處理器也具備某些能力浓镜,但是獲取動態(tài)狀態(tài)依然是一個不好處理的點(diǎn)溃列;
- 所以,目前可以說CSS-in-JS是React編寫CSS最為受歡迎的一種解決方案;
- 目前比較流行的 CSS-in-JS 的庫有
styled-components
膛薛、emotion
, 前者是社區(qū)最流行的 CSS-in-JS庫
styled-components
- 安裝命令:
yarn add styled-components
- 要學(xué)習(xí)styled-components首先要了解 ES6 的標(biāo)簽?zāi)0遄址?詳見JavaScript中的 ES6標(biāo)簽?zāi)0遄址?
- 在styled component中听隐,就是通過這種方式來解析模塊字符串,最終生成我們想要的樣式的
- styled-components的本質(zhì)是通過函數(shù)的調(diào)用相叁,最終創(chuàng)建出一個組件:
- 這個組件會被自動添加上一個不重復(fù)的class遵绰;
import React, { Component } from 'react';
import styled from 'styled-components'
const DivCmp = styled.div`
color:red;
`
export default class App extends Component {
render() {
return (
<DivCmp>
<h2>用戶昵稱</h2>
</DivCmp>
)
}
}
如上圖 styled.div`...` 會給我返回一個 div 類型的標(biāo)簽,styeld 后面跟什么就會返回什么類型,如果是 h1, 返回的就是h1,如果是 span,就返回 span, 如果沒有這種類型會不顯示,
- styled-components會給該class添加相關(guān)的樣式;如上圖生成一個給 div 添加 class="sc-hKMtZM gt0Tnf" 的樣式,這個字符串是唯一的,不會與別的重復(fù)
- props 屬性穿透, props 可以被傳遞給 styled 組件,獲取 props 需要通過${}傳入一個插值函數(shù),props 會作為該函數(shù)的參數(shù),這種方式可以有效解決動態(tài)樣式的問題
import React, { Component } from 'react';
import styled from 'styled-components'
const DivCmp = styled.div`
color:red;
font-size: ${props=>props.fontSize};
text-decoration: ${props=>props.textDecoration};
`
export default class App extends Component {
render() {
return (
<DivCmp fontSize="55px" textDecoration="underline">
<span>用戶昵稱</span>
</DivCmp>
)
}
}
- attrs 函數(shù), 通過styled-components創(chuàng)建出來的 styled 組件都會有一個attrs函數(shù), 該函數(shù)接收一個對象作為參數(shù), 并返回 styled 組件增淹。
- 參數(shù)中我們既可以放置組件的屬性,也能放置組件的樣式
import React, { Component } from 'react';
import styled from 'styled-components'
const HYInput = styled.input.attrs({
placeholder: "請輸入文字", // 這里和HYInput組件中設(shè)置placeholder屬性是一樣的,
bColor: "red" // 這里和在HYInput組件中設(shè)置一個 bColor 屬性是一樣的,可以通過 props 來取
})`
background-color: lightblue;
border-color: ${props => props.bColor}; // 取出 attrs 中的 bColor
color: ${props => props.color};// 取出通過屬性傳過來的 color
`
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
color: "purple"
}
}
render() {
return (
<div>
{/* 從 state 中取出 color 賦值給 color 屬性, 這樣就能動態(tài)改變樣式了 */}
<HYInput type="password" color={this.state.color}/>
</div>
)
}
}
- 繼承, styled組件支持繼承, 當(dāng)很多組件有同樣的樣式時,我們就可以將同樣的樣式抽取成一個基類,然后不一樣的樣式部分再實現(xiàn)子類,通過子類繼承基類來達(dá)到樣式的服用椿访。下面是繼承的例子子組件通過styled(父組件名)的方式來繼承
import React, { PureComponent } from 'react';
import styled from 'styled-components';
const HYButton = styled.button`
padding: 10px 20px;
border-color: red;
color: red;
`
const HYPrimaryButton = styled(HYButton)`
color: #fff;
background-color: green;
`
export default class App extends PureComponent {
render() {
return (
<HYButton>我是普通的按鈕</HYButton>
<HYPrimaryButton>我是主要的按鈕</HYPrimaryButton>
)
}
}
- styled 提供了 ThemeProvider 來共享主題
import React, { PureComponent } from 'react';
import styled, { ThemeProvider } from 'styled-components';
const TitleWrapper = styled.h2`
text-decoration: underline;
color: ${props => props.theme.themeColor};
font-size: ${props => props.theme.fontSize};
`
class Home extends PureComponent {
render() {
return (
<TitleWrapper>我是home的標(biāo)題</TitleWrapper>
)
}
}
export default class App extends PureComponent {
render() {
return (
<ThemeProvider theme={{themeColor: "red", fontSize: "25px"}}>
<Home />
</ThemeProvider>
)
}
}
React 中動態(tài)修改class
- React在JSX給了我們開發(fā)者足夠多的靈活性,你可以像編寫JavaScript代碼一樣虑润,通過一些邏輯來決定是否添加某些class:
<div>
<h2 className={"title " + (isActive ? "active" : "")}> 我是標(biāo)題</h2>
<h2 className={["title ", (isActive ? "active" : "")].join(" ")}> 我是標(biāo)題</h2>
</div>
- 我們也可以借助第三方庫: classnames, 這是一個用于動態(tài)添加 className 的庫
classNames('foo','bar'); // 'foo bar'
classNames('foo',{bar: true}); // 'foo bar'
classNames({'foo-bar': true}); // 'foo bar'
classNames({'foo-bar': false}); // ''
classNames({foo: true},{bar: true}); // 'foo bar'
classNames({foo: true, bar: true}); // 'foo bar'
classNames('foo', {bar: true, duck: false}, 'baz', {quux: true}); // 'foo bar baz quux'
classNames(null,false,'bar',undefined, 0,1,{baz: null},''); // 'bar 1'