《React最佳實踐系列》之css篇

https://user-gold-cdn.xitu.io/2019/9/17/16d3f137782dbd6a?w=1024&h=656&f=jpeg&s=141457

React中的css到底該怎麼寫?這一直是個飽受爭論的話題。本文將結合React CSS的發展史,分別敘述CSS in js與CSS module兩種風格中的最佳實踐。css

行內樣式

首先,facebook提出CSS in js的概念,這很奇怪。 咱們多年來所學的知識都在宣揚關注點分離的重要性,不該該將標記和css混在一塊兒。可是React行內樣式的提出,試圖改變關注點分離這一律念。使其從技術分離向組件分離轉變。html

經過CSS in js有如下兩個優勢:webpack

  1. React將組件做爲應用架構的基礎單元,經過組合組件來建立應用。
  2. 能夠很方便的和邏輯進行交互。

好比這樣(先忽略直接將style對象傳入帶來的性能問題)web

render(){
  return (
    <div style={{fontWeight: this.props.weight ? 'bold' : 'normal'}}>hello world</div>
  )
}
複製代碼

行內樣式也有缺點:設計模式

  1. 不支持僞選擇器和僞元素
  2. 不支持媒體查詢
  3. 不支持樣式回退,由於js對象不支持兩個同名屬性
  4. 不支持動畫
  5. 須要覆蓋常規樣式的時候,可能會須要!important來實現了
  6. 調試不方便
  7. 很關鍵的一點,若是是在服務端渲染的話,會使得頁面體積變得很大

事實證實,雖然行內樣式解決了目標問題,卻引起了更多的問題。bash

CSS Module

若是你認爲行內樣式的方案不適合本身的團隊,但仍然但願儘可能緊密結合樣式與組件,那麼webpackcss-loader就幫了你大忙! 關於webpack和詳細配置,本文再也不贅述。咱們看一下本文主要用到的loaderpluginantd

const HTMLWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  ...
  module: {
    rules: [
      ...
      {
        test: /\.css$/,
        use: [
          { loader: 'style-loader' },
          {
            loader: 'css-loader',
            options: {
              modules: true
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new HTMLWebpackPlugin()
  ]
}
複製代碼

如上配置完之後,在終端啓動項目後,就能夠在網頁中訪問了。接下來咱們開始編寫具體代碼。架構

首先定義一個普通的css文件函數

.button {
  background-color: blue;
  color: #fff;
  padding: 10px 20px;
}
複製代碼

而後在js文件中引入工具

import styles from './index.css'
複製代碼

import語句會導入一個樣式對象,其全部屬性就是index.css中定義的類,這時候運行一下console,開發者工具會輸出一個形如這樣的對象。這個key是根據文件散列值和其餘一些參數生成的。

{
  button: "_2wpxM3yizfwbWee6k0U1D4"
}
複製代碼

接着,咱們能夠用這個對象去設置按鈕的className屬性

const Button = () => (
  <button className={styles.button}>Click it</button>
)
複製代碼

這樣,按鈕的樣式就設置完成了。 咱們打開開發者工具能夠看到,button的類名就是上面的_2wpxM3yizfwbWee6k0U1D4。若是查看頁面頭部,咱們還會發現相同的類名已經注入到頁面中了。

css-loader容許你在js模塊中導入css文件,而且啓用modules標記時,全部類名都只做用於導入它們的模塊。最後,style-loader接收css-loader轉換的結果,並將樣式注入到頁面頭部。

這種用法很是強大,由於咱們擁有了css的完整能力及表現性,又結合了局部做用域類名與顯示依賴的優勢。咱們還能夠像這樣配置localIdentName參數,解決調試時信息不清晰的問題。

module: {
  rules: [
    {
      test: /\.css$/,
      use: [
        { loader: 'style-loader' },
        {
          loader: 'css-loader',
          options: {
            modules: true,
            localIdentName: '[path][name]__[local]--[hash:base64:5]'
          }
        }
      ]
    }
  ]
}
複製代碼

生產環境不須要這樣的類名,更注重性能,所以咱們想要更簡短的類名和散列值。 咱們能夠在生產環境下使用MiniCssExtractPlugin將樣式提取到單獨的css文件,並將其放入CDN,從而得到更好的性能。

css-loader還支持一些關鍵詞。

第一個就是global關鍵詞。給任何類添加:global前綴,意味着請求CSS模塊不要爲當前選擇器加上局部做用域。

例如:

:global .button {
  ...
}
複製代碼

這樣,你能夠應用不須要局部做用域的樣式。好比第三方組件。

第二個就是composes,有了它,就能夠從同個文件或外部依賴中引用類名,將其它類的全部樣式應用於一個元素。

例如:

.text-red{
  color: red;
}

.button {
  composes: text-red;
  background-color: blue;
  color: #fff;
  padding: 10px 20px;
}
複製代碼

最終,button類的全部規則以及composes聲明的全部規則都能做用於元素。

這個特性很是強大,並且原理很巧妙。你可能覺得它和SASS@extend方法同樣,只是將組合類複製到引用它們的位置,其實不是這樣。簡單來說,全部組合類名都是逐個應用到DOM中的組件上。

以咱們的示例來講,代碼以下:

<button class="_2wpxM3yizfwbWee6k0U1D4 Sf8w9cFdQXdRV_i9dgcOq">Click it</button>
複製代碼

注入頁面的css以下所示:

.Sf8w9cFdQXdRV_i9dgcOq {
  color: red;
}

._2wpxM3yizfwbWee6k0U1D4 {
  background-color: blue;
  color: #fff;
  padding: 10px 20px;
}
複製代碼

原子級CSS

原子級CSS又稱函數式CSS,是CSS的一種使用方式,即每一個類只有一條規則。

例如,能夠用一個類來設置底部外邊距爲0:

.mb0 {
  margin-bottom: 0;
}
複製代碼

而後用另外一個類設置font-weight爲bold

.fwb {
  font-weight: bold;
}
複製代碼

而後將這些原子類用在元素上:

<span class="mb0 fwb">Hello World</span>
複製代碼

這種技巧存在爭議,但很高效。一方面,類是在CSS文件中定義,卻在視圖層組合,每次修改元素的樣式都要同時修改兩個地方;另外一方面,它能夠超快地搭建頁面。

其實,只要全部基本規則都定好,將這些類應用於元素或者用它們生成新的樣式都很是快,這是一大優勢。其次,使用原子級CSS能夠控制css文件的大小,由於建立新組建時能夠複用已有類的樣式,不須要編寫新樣式,這對性能頗有好處。

原子級CSS Module

咱們能夠將CSS Module與原子級CSS結合起來使用。

查看如下示例:

.title {
  composes: mb0 fwb;
}

<span class="title">Hello World</span>
複製代碼

這種作法很是好,由於樣式邏輯仍然保留在CSS中,同時CSS模塊利用composes將全部單個類聚合到一個類中。

上述代碼的渲染結果以下所示:

<span class="title--3JCJR mb0--21SyP fwb--1JRhZ">Hello World</span>
複製代碼

此處的titlemb0以及fwb都是自動加到元素上的。而且它們都只做用於局部。這樣,咱們就用上了CSS Module的全部優點。

styled-components

最後,讓咱們看一下CSS in js中的王者——styled-components。 這個庫能夠說考慮到了其餘組件樣式庫遇到的全部問題。

在安裝styled-components以後,咱們能夠這樣使用它:

import styled from 'styled-components'

const Button = styled.button` background-color: blue; color: #fff; padding: 10px 20px; `
複製代碼

能夠看到它使用了模板字符串,這意味着它能夠用js的所有能力爲元素添加樣式。 這種看似奇怪的語法會返回普通的React組件Button,它渲染了一個按鈕元素,並加上了模板中定義的樣式。先建立惟一的類名,再將它加到元素上,最後向頁面文檔頭部注入相應的樣式。至此,樣式生效了。

渲染的組件以下所示:

<button class="kYvFOg">Click it</button>
複製代碼

頁面上添加的樣式以下:

.kYvFOg {
  background-color: blue;
  color: #fff;
  padding: 10px 20px;
}
複製代碼

這個庫的優勢以下在於支持幾乎全部的css特性。好比,它支持SASS風格的僞類語法:

const Button = styled.button` background-color: blue; color: #fff; padding: 10px 20px; &:hover { background-color: #fff; color: blue; } `
複製代碼

它也支持媒體查詢:

const Button = styled.button` background-color: blue; color: #fff; padding: 10px 20px; &:hover { background-color: #fff; color: blue; } @media (max-width: 480px) { padding: 10px 10px; } `
複製代碼

它還能夠很方便地覆蓋樣式,並設置不一樣屬性來屢次複用該組件。當你使用了形如antd之類的UI庫,並但願自定義一些樣式時,這個優勢會很是明顯。

結尾

CSS in or not in js ? this is a question.

我以爲沒有絕對的最佳實踐,只有在工程迭代的過程當中,不停的找尋最適合工程、最適合團隊的方案,這纔是最明智的選擇。

參考文獻:

  • 《React設計模式與最佳實踐》第七章
相關文章
相關標籤/搜索