由于React推崇組件模式,因此會要求HTML、CSS和JavaScript混合在一起,雖然這與過去的關注點分離正好相反,但是更有利于組件之間的隔離。React已將HTML用JSX封裝,而對CSS只進行了較弱的封裝,僅僅給出了基本的樣式設置。不過,好在第三方庫提供了CSS in JS的解決方案,讓開發者能更高效的書寫組件的樣式,促進CSS工程化的發展。
## 一、內聯樣式
  在React中的元素都包含style屬性,用來定義內聯樣式。style的屬性值是一個對象而不是一段字符串,該對象的屬性就是CSS屬性,但屬性名要用小駝峰的方式命名,例如line-height改成lineHeight,具體如下所示。
~~~js
class Btn extends React.Component {
render() {
let btnStyle = {
height: 40,
lineHeight: 1.5,
WebkitBorderRadius: "10%"
};
return <button style={btnStyle}>提交</button>;
}
}
~~~
  在CSS中,有些屬性是需要單位的,例如代碼中的height,如果沒有寫明,那么React會自動給它們的數值加上像素(px)單位。還有一點要注意,屬性名不會自動補全瀏覽器前綴,除了IE的ms前綴是純小寫之外,其它前綴的首字母都得是大寫,例如Moz、O和Webkit。
## 二、CSS類
  元素的className屬性能夠設置CSS類,它的值是一段字符串,如下代碼所示,為元素添加了一個名為btn的CSS類。
~~~js
class Btn extends React.Component {
render() {
return <button className="btn">提交</button>;
}
}
~~~
  如果要動態處理(增、刪或改)元素的CSS類,那么借助第三方的classnames庫就能大大降低開發復雜度。下面有兩個示例,完成了相同的功能,都會根據組件的active狀態(布爾值)決定是否添加一個名為active的CSS類。
~~~js
class Btn1 extends React.Component {
render() {
let className = "btn";
if (this.state.active) className += " active";
return <button className={className}>提交</button>;
}
}
class Btn2 extends React.Component {
render() {
let btnClass = classNames({
btn: true,
active: this.state.active
});
return <button className={btnClass}>提交</button>;
}
}
~~~
  Btn1組件通過條件語句和字符串拼接實現了CSS類的添加,而Btn2組件的實現方式更為優雅和清晰,只需要調用classNames()函數就行。該函數能接收一個由CSS類組成的對象,其中屬性名就是類名,屬性值是布爾類型的,當為true時,就添加該屬性,否則忽略。
## 三、CSS in JS
  由于前端的職能和項目規模正在擴大,因此工程化是大勢所趨,而CSS的先天缺陷卻在阻礙這一發展進程,目前碰到的主要問題如下所列。
  (1)全局作用域,任意一個CSS規則都對整個頁面有效,當多個CSS規則發生沖突(即樣式污染)時,會根據計算出的特殊性再決定采用哪個CSS規則。雖然可以通過OOCSS、BEM等規范避免多人協作時的代碼沖突,但畢竟是自選方案,需要依靠團隊的執行力度,無法在語言或工具級別強制實施。
  (2)缺少編程特性,沒有變量、循環或函數等編程語言所需的語法,使得樣式表有很多冗余代碼。雖然社區發展的CSS預處理器(Sass、Less等)和CSS后處理器(PostCSS)有效緩解了這種狀況,但是無法共享CSS處理器和JavaScript中的變量,不能滿足某些特定復雜的場景。
  (3)沒有依賴管理,在引入一個組件時,應該只加載與之關聯的CSS,而不是像現在這樣將整個CSS文件中的樣式都導入進來。目前,市面上已經出現了好多用JavaScript管理CSS依賴的第三方庫,例如styled-components、css-loader等。
**1)React的應對**
  在將CSS應用到React中時,為了能有效的規避上述問題,引入了CSS in JS的編程思想,即用JavaScript編寫CSS,相關的解決方案有40多種之多,可謂百家爭鳴,這其中又分為拋棄CSS和沿用CSS兩種。
  第一節所講解的style屬性(內聯樣式)就徹底拋棄了CSS,完全用JavaScript控制CSS,不僅CSS屬性的命名方式不同,而且還無法使用偽類、偽元素、媒體查詢等CSS特性。
  而CSS Modules與前者不同,依然采用JavaScript和CSS分離的寫法,不僅沿用現有的CSS生態和JavaScript模塊化的能力,還支持CSS預處理器的語法,并且能夠零成本對接遺留項目。通過將選擇器編譯成唯一的CSS類名后,就能實現CSS模塊化。不過,由于采用了BEM(Block Element Modifier)命名規范,因此得到的CSS類名將會比較復雜,并且沒有充分利用JavaScript來控制樣式,例如沒有打通JavaScript和CSS中的變量。
  其實這兩種解決方案只有在適合的場景中,才能最大限度的發揮它的作用,真正意義上的銀彈并不存在,本節接下來要介紹一個介于這兩者之間的第三方庫:styled-components。
**2)標簽模板**
  styled-components的實現基于ES6新增的[標簽模板](https://www.cnblogs.com/strick/p/10173486.html),移除了元素和樣式之間的映射,通過JavaScript完全控制了CSS,不僅支持所有的CSS特性(例如偽類、媒體查詢、動畫等),還能自動添加瀏覽器前綴,但不支持Less、Sass等CSS預處理器的語法。下面的示例創建了一個帶樣式的Content組件,之所以定義在render()方法之外,是為了避免緩存失效,提升渲染速度。
~~~js
const Content = styled.button`
color: red;
appearance: none;
`;
class Btn extends React.Component {
render() {
return <Content>提交</Content>;
}
}
~~~
  在將Btn組件掛載到DOM中后,會渲染出一個包含CSS類的\<button>元素,下面是生成的HTML代碼。
~~~html
<style>
.iCQFTl {
color: red;
-webkit-appearance: none;
}
</style>
<button class="iCQFTl">提交</button>
~~~
  由styled-components為該按鈕生成了一個名稱唯一的CSS類iCQFTl,并且被內嵌到了頁面中。注意,appearance是一個實驗中的CSS屬性,用于設置元素的默認樣式,在不同瀏覽器中會被styled-components添加不同的前綴,例如上面CSS規則中的-webkit。
**3)樣式繼承**
  通過構造函數styled()能夠繼承指定組件的樣式,例如可以用下面的方式繼承上例的Content組件,并額外聲明背景顏色。
~~~js
const BgContent = styled(Content)`
background: yellow;
`;
~~~
**4)屬性傳遞**
  通過組件的props能夠調整其自身的樣式,如下代碼所示,占位符內是一個箭頭函數,其參數就是傳遞進來的props。
~~~js
const AttrContent = styled.button`
background: ${props => props.active ? "blue" : "white"}
`;
class Btn extends React.Component {
render() {
return <AttrContent active>提交</AttrContent>;
}
}
~~~
**5)選擇器嵌套**
  styled-components使用了一個輕量級的CSS預處理器:stylis,用JavaScript實現了選擇器的嵌套,如下代碼所示,其中&符號表示父級選擇器。
~~~js
const ProContent = styled.button`
&:hover {
color: yellow;
}
`;
~~~
  限于篇幅原因,本節只列出了styled-components的幾個基礎功能,其他諸如主題、附加屬性、Refs、動畫等功能可以參考[官方文檔](https://www.styled-components.com/docs)。
  styled-components開辟了一種新的控制CSS的方式,不但保持了原生CSS的寫法,而且還忽略了CSS和HTML元素之間的關聯,讓React組件更簡單。
*****
> 原文出處:
[博客園-React躬行記](https://www.cnblogs.com/strick/category/1455720.html)
[知乎專欄-React躬行記](https://zhuanlan.zhihu.com/pwreact)
已建立一個微信前端交流群,如要進群,請先加微信號freedom20180706或掃描下面的二維碼,請求中需注明“看云加群”,在通過請求后就會把你拉進來。還搜集整理了一套[面試資料](https://github.com/pwstrick/daily),歡迎瀏覽。

推薦一款前端監控腳本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不僅能監控前端的錯誤、通信、打印等行為,還能計算各類性能參數,包括 FMP、LCP、FP 等。
- ES6
- 1、let和const
- 2、擴展運算符和剩余參數
- 3、解構
- 4、模板字面量
- 5、對象字面量的擴展
- 6、Symbol
- 7、代碼模塊化
- 8、數字
- 9、字符串
- 10、正則表達式
- 11、對象
- 12、數組
- 13、類型化數組
- 14、函數
- 15、箭頭函數和尾調用優化
- 16、Set
- 17、Map
- 18、迭代器
- 19、生成器
- 20、類
- 21、類的繼承
- 22、Promise
- 23、Promise的靜態方法和應用
- 24、代理和反射
- HTML
- 1、SVG
- 2、WebRTC基礎實踐
- 3、WebRTC視頻通話
- 4、Web音視頻基礎
- CSS進階
- 1、CSS基礎拾遺
- 2、偽類和偽元素
- 3、CSS屬性拾遺
- 4、浮動形狀
- 5、漸變
- 6、濾鏡
- 7、合成
- 8、裁剪和遮罩
- 9、網格布局
- 10、CSS方法論
- 11、管理后臺響應式改造
- React
- 1、函數式編程
- 2、JSX
- 3、組件
- 4、生命周期
- 5、React和DOM
- 6、事件
- 7、表單
- 8、樣式
- 9、組件通信
- 10、高階組件
- 11、Redux基礎
- 12、Redux中間件
- 13、React Router
- 14、測試框架
- 15、React Hooks
- 16、React源碼分析
- 利器
- 1、npm
- 2、Babel
- 3、webpack基礎
- 4、webpack進階
- 5、Git
- 6、Fiddler
- 7、自制腳手架
- 8、VSCode插件研發
- 9、WebView中的頁面調試方法
- Vue.js
- 1、數據綁定
- 2、指令
- 3、樣式和表單
- 4、組件
- 5、組件通信
- 6、內容分發
- 7、渲染函數和JSX
- 8、Vue Router
- 9、Vuex
- TypeScript
- 1、數據類型
- 2、接口
- 3、類
- 4、泛型
- 5、類型兼容性
- 6、高級類型
- 7、命名空間
- 8、裝飾器
- Node.js
- 1、Buffer、流和EventEmitter
- 2、文件系統和網絡
- 3、命令行工具
- 4、自建前端監控系統
- 5、定時任務的調試
- 6、自制短鏈系統
- 7、定時任務的進化史
- 8、通用接口
- 9、微前端實踐
- 10、接口日志查詢
- 11、E2E測試
- 12、BFF
- 13、MySQL歸檔
- 14、壓力測試
- 15、活動規則引擎
- 16、活動配置化
- 17、UmiJS版本升級
- 18、半吊子的可視化搭建系統
- 19、KOA源碼分析(上)
- 20、KOA源碼分析(下)
- 21、花10分鐘入門Node.js
- 22、Node環境升級日志
- 23、Worker threads
- 24、低代碼
- 25、Web自動化測試
- 26、接口攔截和頁面回放實驗
- 27、接口管理
- 28、Cypress自動化測試實踐
- 29、基于Electron的開播助手
- Node.js精進
- 1、模塊化
- 2、異步編程
- 3、流
- 4、事件觸發器
- 5、HTTP
- 6、文件
- 7、日志
- 8、錯誤處理
- 9、性能監控(上)
- 10、性能監控(下)
- 11、Socket.IO
- 12、ElasticSearch
- 監控系統
- 1、SDK
- 2、存儲和分析
- 3、性能監控
- 4、內存泄漏
- 5、小程序
- 6、較長的白屏時間
- 7、頁面奔潰
- 8、shin-monitor源碼分析
- 前端性能精進
- 1、優化方法論之測量
- 2、優化方法論之分析
- 3、瀏覽器之圖像
- 4、瀏覽器之呈現
- 5、瀏覽器之JavaScript
- 6、網絡
- 7、構建
- 前端體驗優化
- 1、概述
- 2、基建
- 3、后端
- 4、數據
- 5、后臺
- Web優化
- 1、CSS優化
- 2、JavaScript優化
- 3、圖像和網絡
- 4、用戶體驗和工具
- 5、網站優化
- 6、優化閉環實踐
- 數據結構與算法
- 1、鏈表
- 2、棧、隊列、散列表和位運算
- 3、二叉樹
- 4、二分查找
- 5、回溯算法
- 6、貪心算法
- 7、分治算法
- 8、動態規劃
- 程序員之路
- 大學
- 2011年
- 2012年
- 2013年
- 2014年
- 項目反思
- 前端基礎學習分享
- 2015年
- 再一次項目反思
- 然并卵
- PC網站CSS分享
- 2016年
- 制造自己的榫卯
- PrimusUI
- 2017年
- 工匠精神
- 2018年
- 2019年
- 前端學習之路分享
- 2020年
- 2021年
- 2022年
- 2023年
- 2024年
- 日志
- 2020