React中的樣式

  • 目前整個前端已經(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ā)的方式也受到了很多的批評:
  • 批評聲音雖然有敞掘,但是在我們看來很多優(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'
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末成玫,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子拳喻,更是在濱河造成了極大的恐慌哭当,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冗澈,死亡現(xiàn)場離奇詭異钦勘,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)亚亲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進(jìn)店門彻采,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人捌归,你說我怎么就攤上這事肛响。” “怎么了惜索?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵特笋,是天一觀的道長。 經(jīng)常有香客問我巾兆,道長猎物,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任角塑,我火速辦了婚禮霸奕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘吉拳。我一直安慰自己浙滤,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布僻孝。 她就那樣靜靜地躺著觅够,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天,我揣著相機(jī)與錄音剪侮,去河邊找鬼。 笑死洛退,一個胖子當(dāng)著我的面吹牛瓣俯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播兵怯,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼彩匕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了媒区?” 一聲冷哼從身側(cè)響起驼仪,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎袜漩,沒想到半個月后绪爸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宙攻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年奠货,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片座掘。...
    茶點(diǎn)故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡递惋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出雹顺,到底是詐尸還是另有隱情,我是刑警寧澤廊遍,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布嬉愧,位于F島的核電站,受9級特大地震影響喉前,放射性物質(zhì)發(fā)生泄漏没酣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一卵迂、第九天 我趴在偏房一處隱蔽的房頂上張望裕便。 院中可真熱鬧,春花似錦见咒、人聲如沸偿衰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽下翎。三九已至缤言,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間视事,已是汗流浹背胆萧。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留俐东,地道東北人跌穗。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像虏辫,于是被迫代替她去往敵國和親蚌吸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容