CSS in JS 很棒, 但是如何方便的處理CSS偽類

超級大柱子發表於2018-10-18

不知道掘金至不支援GIF

CSS in JS 很棒, 但是如何方便的處理偽類(Pseudo-classes)? react-dom-pseudo 提供一個類似 react-motion 方式的元件,方便的為 react-dom 物件提供類似 CSS 的偽類.

我們首先用 npm 安裝:

$ npm install --save react-dom-pseudo
複製程式碼

APIs

react-dom-pseudo 支援以下偽類:

Props 模擬偽類 說明 預設值 必須
merge 是否使用 style 和 其他狀態的 style 進行合併 true
disable 是否取消事件監聽 false
style 預設樣式 undefined
linkStyle :link 未被點選之前的樣式 undefined
visitedStyle :visited 被點選過的樣式 undefined
focusStyle :focus input 等型別元素 onFocus 時的樣式 undefined
hoverStyle :hover 滑鼠移入時顯示的樣式 undefined
activeStyle :active 滑鼠或者觸屏點選時的樣式 undefined
disableStyle 當取消事件監聽時的樣式 undefined
alwayStyle 會和所有樣式合併,並且覆蓋重複的樣式屬性 undefined

他們會根據事件的觸發,和 style 合併返回, 如 {...style, ...activeStyle}, 只有存在的樣式會進行合併

樣式的組合規則: {...style, ...linkStyle, ...eventStyle, ...disableStyle, ...alwayStyle}

使用

import Pseudo from 'react-dom-pseudo';

export default () => {
  return (
    <div>
      <div>example:</div>
      <Pseudo
        style={sheet.input}
        hoverStyle={sheet.inputHover}
        focusStyle={sheet.inputFocus}
      >
        {events => <input {...events} />}
      </Pseudo>
    </div>
  );
};

// CSS in js
const sheet = {
  input: {
    fontSize: '14px',
    border: '1px solid rgba(0,0,0,0)',
    background: '#f3f3f3',
    // 啟用過渡動畫
    transition: 'all 0.2s ease-out',
  },
  inputHover: {
    background: '#f0f0f0',
  },
  inputFocus: {
    border: '1px solid rgba(0,0,0,0.1)',
    background: '#f0f0f3',
    transitionTimingFunction: 'ease-in',
  },
};
複製程式碼

其中做了什麼?

Pseudo 的 renderProps 中包含以下事件

  • onClick: 用來模擬 :link 和 :visited 偽類
  • onFocus\Blur: 用來模擬 :focus 偽類
  • onMouseEnter\Leave: 用來模擬 :hover 偽類
  • onMouseDown\Up: 用來模擬 :active 偽類

如果專案在移動端執行,就會把 onMouse? 相關的事件替換成 onTouch? 以相容移動端

renderProps 的方式相比我直接定義一個 Input 元件有什麼優勢?

我們先看看如果我們直接定義一個 Input 元件,來模擬 :hover 偽類

// 以下程式碼直接在 markdown 中編寫,並無經過執行,僅用於闡述觀點
class Input extend React.Component {
  state = {
    hover: false
  }
  handleMouseEnter = ()=>{
    this.setState({ hover: true });
  }
  handleMouseLeave = ()=>{
    this.setState({ hover: false });
  }
  render(){
    return <input style={this.state.hover?{...this.props.style, ...this.props.hoverStyle}:this.props.style} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} />
  }
}
複製程式碼

然後我們在專案中使用:

<Input style={inputStyle} hover={inputHoverStyle} />
複製程式碼

一切看起來不錯,但是它不利於擴充套件, 例如:我們如果需要給一個 divSignButton 也新增以上功能,我們需要再寫一個以上元件

當然,我們也可以使用 HOC 的方式, 編寫一個 withHover 的元件, 即便如此,也需要在使用之前建立一個新的元件:

const SignButton = withHover(SignButton);
複製程式碼

對比之下,就沒有 RenderProps 的方式優雅:

<Pseudo style={inputStyle} hoverStyle={inputHoverStyle}>
  {events => <input {...events} />}
</Pseudo>
<Pseudo style={inputStyle} hoverStyle={inputHoverStyle}>
  {events => <div {...events} />}
</Pseudo>
<Pseudo style={inputStyle} hoverStyle={inputHoverStyle}>
  {events => <SignButton {...events} />}
</Pseudo>
複製程式碼

react-dom-pseudo 還可以更簡化麼?

由於如果子物件是一個 div 或是一個 陣列 時,覺得使用 childrenFuncion 的意義不大,所以可以把簡寫:

// 可以簡寫成如下, 此時 Pseudo 是一個 div元件
<Pseudo style={inputStyle} hoverStyle={inputHoverStyle} />

// 同理,多個子元素,也可以這樣
<Pseudo style={inputStyle} hoverStyle={inputHoverStyle}>
  <p>多個子元素</p>
  <img src="xxx"/>
  <div>父級相當於一個div</div>
</Pseudo>
複製程式碼

我如何獲取 hover、active 等狀態,做除了樣式之外的其他事件?

renderProps 的引數還有第二個,是 Pseudo 內部的 state, 我們可以獲取它之後做其他事件, 如下面的例子,根據 hover 的狀態我們修改 divinnerText,

state 有 4 個物件 { hover, focus, active, visited }

<Pseudo style={inputStyle} hoverStyle={inputHoverStyle}>
  {(events, state) => {
    const mouseState = state.hover ? 'mouseIn' : 'mouseOut';
    return <div {...events}>{mouseState}</div>;
  }}
</Pseudo>
複製程式碼

如何臨時遮蔽事件監聽?

Pseudo 把 disable 設定成 true

<Pseudo disable />
複製程式碼

如何在所有狀態樣式的外層新增樣式?

Pseudo 有一個 alwayStyle 屬性,最終返回的樣式是這樣的 {...style, ...otherStyle, ...alwayStyle}

<Pseudo alwayStyle={style} />
複製程式碼

CSS in JS 好用麼?

因人而異,我覺得比寫 csssass 更好一些,其原因有以下幾點:

  1. 許多動畫庫,如 react-motion, react-spring 等,都會需要操作 style 物件, 它的樣式可能分別會存在 css 和 js 中,此時就沒有 CSS in JS 簡潔;
  2. CSS in JS 可以讓元件相關的程式碼在一個檔案裡閉合,我們修改一個元件時,不需要來回切換檔案;
  3. 如果我們專案較大,需要切分模組,CSS in JS 會比傳統的 css 檔案更好和元件一起切分.

如何解決一些 CSS in JS 寫起來麻煩的事情?

  1. sass 的顏色混合等功能,可以使用 mix-color 之類的庫輕鬆解決;
  2. sass 的自定義變數,在 CSS in JS 中可以很輕鬆的定義一個 globalStyles 物件達到;
  3. css 的偽類,以前用元件 state 的寫法編寫起來比較麻煩、重複,可以用 react-dom-pseudo 解決.

react-dom-pseudo 支援 react-native 嗎?

由於 react-dom-pseudo 用到了 ReactDOM 的事件,所以不支援 react-native, 有需要的朋友可以 fork 一份把DOM事件改成RN的 touchble 事件

最後,謝謝閱讀:)

MIT License

相關文章