了解 CSS in JS 的起源、現況,還有其在 React Native 中的用法,順便學習一下 Flexbox。
Facebook Infrastucture Team 的知名開發者 (Christopher Chedeau) vjeux 在 CSS in JS 的投影片裡面指出了 CSS 的七大問題:
為了解決 CSS 的這些缺點,各種新的解決辦法不斷被提出,其中大部分是 CSS in JS 的做法,或加上一些 Component-Based 又或是 Babel 編譯的解決方案:
不管是存在了比較久的:
或是近幾個月才出現而且聲勢浩大的:
甚至也有今年十二月才竄出的:
還有更多已經消亡的專案,直到今日,已經有了漫長的討論,但在這個領域仍然看不到明顯的贏家。從 reactjs/core-notes 的這裡跟這裡也看得到 Facebook 積極的在做相關的實驗,但目前並沒有滿意的結果。
而且用 JavaScript 寫 Style 雖然解決了不少問題,但也造成了很多問題,例如以下的 Style 是不能直接支援的:
const styles = {
color: '#fff',
':hover': {
'color': 'red',
},
'@media (max-width: 480px)': {
width: '100%',
},
};
為了支援 ::after
、:hover
、@media (max-width: 480px)
等等 CSS 既有的寫法,這些 Library 需要用到很多的 Workaround,這部 Inline Styles 的影片有講到一些相關的主題。以筆者的觀點來看,最好是選一種不會被綁死的做法比較保險。
不過至少在做 Component 放 npm 時,筆者會想選用 Inline Style,因為不能假設使用者會使用 Webpack、有一樣的 Webpack 設定檔,也不能假設使用者會使用 Sass、Less、Stylus、PostCSS 任何一種,用 Inline Style 是唯一一個 Zero Configuration 的方式。
另一個值得一提的是 CSS Modules,它被內建在 Webpack 的 css-loader
裡面,可以在 class 名稱加上一個 hash 來避免全域的碰撞:
.btn {}
import styles from './btn.module.css';
styles.btn // btn_1afe3
如此一來還是可以開心地寫 CSS,這也是筆者最常用的方法之一。
可以直接把 Style Object 丟進去 style
Prop 來處理樣式:
<Text style={{
width: 38,
height: 38,
}} />
不過在 React Native 我們一般用 StyleSheet.create
來建立 Style:
const styles = StyleSheet.create({
base: {
width: 38,
height: 38,
},
background: {
backgroundColor: '#222222',
},
active: {
borderWidth: 2,
borderColor: '#00ff00',
},
});
StyleSheet.create
有幾個明顯的優點:
ReactNativePropRegistry
註冊產生一個 id,就不用重複建立物件,也不用重複丟過去跟 Native 的 Bridge所有的 CSS 屬性都需要寫成 camelCase (駝峰式命名),例如:
background-color -> backgroundColor
border-width -> borderWidth
border-color -> borderColor
接著就可以在 styles
上面取用要傳入到 Component style
Prop 的屬性:
<Text style={styles.base} />
<View style={styles.background} />
React Native 的 Style Prop 跟 React 有點不太一樣,它可以接 Array,屬性衝突時後面的會覆蓋掉前面的屬性:
<View style={[styles.base, styles.background]} />
而且它會忽略 Array 中的 Falsy 值,所以我們可以這樣做:
<View
style={[
styles.base,
this.state.active && styles.active,
]}
/>
必須注意的是,React Native 實作的並不是完整的 CSS 功能,而只是一個子集合,可以用的屬性可以參考 React Native Styling Cheat Sheet。
React Native 主要是靠 Flexbox 來排版,而下面有實作 Flexbox 的跨平台引擎 Yoga (以前叫做 css-layout)。
曾經,處理 Layout 對不熟 CSS 的前端工程師來講是個刻骨銘心的痛,float
、position
跟 inline-block
各有各的毛病,十分不直覺的潛規則一個又一個。
不管是 margin: 0 auto;
還是垂直置中的七種方式都讓當初的我充滿各種問號,一旦把東西排到對的位置,就再也不敢動它。
更別說來到了行動時代,我們還有 RWD (Responsive Web Design) 得要處理。
但現在與兩三年前不同,是個幸福的時代,一代代的 IE 漸漸走入歷史,而新的標準正在建立,迎來了新的 CSS 排版方式 Flexbox:
在寫 Flexbox 時,首先我們要分辨的是 Flex Container 與 Flex Item:
它們彼此有些不同的屬性可以設定,而這邊的 Main Axis 是 Item 排列的軸線,Cross Axis 是與 Main Axis 正交的軸線。
.container {
flex-direction: row | row-reverse | column | column-reverse;
}
.container {
flex-wrap: nowrap | wrap | wrap-reverse;
}
其中 nowarp
是單行,wrap
是要換行。
所以,我們只要用
.container {
justify-content: center;
align-items: center;
}
就可以達到垂直水平置中!
例如一個這樣的範例:
export default class Demo extends Component {
render() {
return (
<View style={styles.container}>
<View style={styles.item} />
<View style={styles.item} />
<View style={styles.item} />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#EADE31',
},
item: {
width: 100,
height: 100,
margin: 10,
backgroundColor: '#7FEA9B',
},
});
就能輕鬆的把 Item 都放在最中間:
flex-shrink:如果 Container 空間不夠時,分配給 Item 壓縮的比例。
flex-basis:設定不該被拿進去分配的基本長度。flex-basis: 0;
會把全部拿去分配,flex-basis: auto;
會扣掉他原本的長度再拿去分配。
以上這三個可以縮寫成
.item {
flex: <flex-grow> <flex-shrink> <flex-basis>;
}
而它的預設值為 0 1 auto
;
到這邊就把 Flexbox 重要的部分介紹完了。
(特別感謝 CSS-Tricks 提供的圖,以及它們令人感動的 License)
在 Can I Use 上看得到,Flexbox 的瀏覽器支援度已經來到 97.32%。
這些是不錯的學習資源:
Flexbox 乍看之下一堆屬性,但實際學起來其實是簡單明瞭的,用這樣的方式來去處理 App 的 Layout 更能比較輕鬆的面對世界上破千種以上的手機跟它各自的長寬比。尤其筆者本身是個 Web 開發者,不是 iOS、Android 的開發者,能直接開始調整樣式的這個優點是非常有價值的。