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} />
複製程式碼
一切看起來不錯,但是它不利於擴充套件, 例如:我們如果需要給一個 div
或 SignButton
也新增以上功能,我們需要再寫一個以上元件
當然,我們也可以使用 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 的狀態我們修改 div
的 innerText
,
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 好用麼?
因人而異,我覺得比寫 css
和 sass
更好一些,其原因有以下幾點:
- 許多動畫庫,如
react-motion
,react-spring
等,都會需要操作 style 物件, 它的樣式可能分別會存在 css 和 js 中,此時就沒有 CSS in JS 簡潔; - CSS in JS 可以讓元件相關的程式碼在一個檔案裡閉合,我們修改一個元件時,不需要來回切換檔案;
- 如果我們專案較大,需要切分模組,CSS in JS 會比傳統的 css 檔案更好和元件一起切分.
如何解決一些 CSS in JS 寫起來麻煩的事情?
sass
的顏色混合等功能,可以使用mix-color
之類的庫輕鬆解決;sass
的自定義變數,在 CSS in JS 中可以很輕鬆的定義一個globalStyles
物件達到;- css 的偽類,以前用元件 state 的寫法編寫起來比較麻煩、重複,可以用
react-dom-pseudo
解決.
react-dom-pseudo
支援 react-native
嗎?
由於 react-dom-pseudo
用到了 ReactDOM
的事件,所以不支援 react-native
, 有需要的朋友可以 fork 一份把DOM事件改成RN的 touchble
事件
最後,謝謝閱讀:)