不久以前我重構了一個古老的項目,總結了一些js方面的想法,不過對於一個前端項目而言不只僅只由js組成的嘛,上學的時候老師和我說HTML+CSS+JS對應的是頁面的骨架、皮膚和肌肉。既然骨架咱們有了,肌肉也聊完了,今天咱們就來聊聊「皮膚」吧。javascript
因爲我重構的是一個react-native項目,因此咱們先來講說在react-native上是怎麼寫樣式的吧,和傳統的web不同的是,在react-native上面是沒有css代碼,不過得益於Yoga,咱們能夠在客戶端上像寫css同樣的去書寫咱們的樣式。咱們來看看react-native文檔上是怎麼說的吧:css
在React Native中,你並不須要學習什麼特殊的語法來定義樣式。咱們仍然是使用JavaScript來寫樣式。全部的核心組件都接受名爲style的屬性。這些樣式名基本上是遵循了web上的CSS的命名,只是按照JS的語法要求使用了駝峯命名法,例如將background-color改成backgroundColor。前端
style屬性能夠是一個普通的JavaScript對象。這是最簡單的用法,於是在示例代碼中很常見。你還能夠傳入一個數組——在數組中位置居後的樣式對象比居前的優先級更高,這樣你能夠間接實現樣式的繼承。java
沒錯,你幾乎不須要什麼成本就能夠按照寫css同樣的寫法去寫咱們的rn樣式,咱們來看一下文檔中的例子:react
import React, { Component } from 'react'; import { AppRegistry, StyleSheet, Text, View } from 'react-native'; export default class LotsOfStyles extends Component { render() { return ( <View> <Text style={styles.red}>just red</Text> <Text style={{ color: 'blue', fontWeight: 'bold', fontSize: 30, }}>just bigblue</Text> <Text style={[styles.bigblue, styles.red]}>bigblue, then red</Text> <Text style={[styles.red, styles.bigblue]}>red, then bigblue</Text> </View> ); } } const styles = StyleSheet.create({ bigblue: { color: 'blue', fontWeight: 'bold', fontSize: 30, }, red: { color: 'red', }, }); AppRegistry.registerComponent('LotsOfStyles', () => LotsOfStyles);
在上面的demo中,咱們有兩種方式去寫咱們的樣式,它和咱們在寫css時候遇到的外聯式樣式、內聯式樣式很類似,而項目中咱們老是習慣將樣式和頁面分離,而後把他們都放在另一個style.js文件中。這是個很是不錯的習慣,可是也形成了一些困擾。web
面對一個頁面,我該怎麼去模塊化它的樣式呢?在以前的項目中雖然作到了樣式和頁面的分離,讓頁面「看起來」乾淨了不少,可是在 style.js 文件中仍然是雜亂的代碼,大量重複的變量、重複的內容、重複的聲明。。。這個時候又有同窗說了,咱們能夠把這些公共的變量、代碼分離出來放到一個主題文件中呀,因而項目中除了各個頁面的style.js以外又在全局出現了一個theme.js文件,在這裏你們愉快的把諸如顏色、大小、佈局等公共的代碼放了進來。react-native
這看似解決了style.js裏重複冗餘的代碼,可是也會讓個人import變得混亂:數組
import { StyleSheet } from 'react-native'; import { px, COLOR_BG_RED, COLOR_BG_GREEN, STYLE_FR_VC_HSB, STYLE_FR_VC_HC, STYLE_FR_VC_HFS, } from 'MyStyle'; export default StyleSheet.create({ // TODO });
看到這裏,我想你除了知道我import進來了兩個顏色以外,對於其它變量會一頭霧水吧,除非你去MyStyle模塊裏面親眼看一下才會知道真正引入進來的是些什麼了,若是這裏的樣式特別的多的話,除了再新建一個sytle.js以外,你就只能每次回到頭部去看看本身引入了些什麼。這是我不能忍受的。。。less
除了因爲一次性引入太多的公共樣式致使我要來回滑動以外,當我再去寫一個新的styel.js文件時,複製這麼多引入也是一件頭疼的事情,那麼我能不能每次只須要寫一行import呢?若是個人樣式都是按固定規則分類放好的是否是每次就能夠只import這幾個類了呢?模塊化
常常寫css的同窗必定注意過樣式的書寫順序,某一類的屬性寫在一塊兒,雖然在web中,這樣寫是爲了優化css引擎,可是這也體現出了樣式是有必定類型的,控制顏色的、控制邊距的、控制佈局的,那麼咱們的公共變量是否是也能夠按照這樣的規則來聲明呢?
import { color, size, layout } from 'MyStyle';
這樣咱們文件的頭部是否是就清晰多了呢?在寫代碼的時候,也不須要再關心我以前引入了些什麼了,只要只要關注咱們要寫什麼就好了:
export default StyleSheet.create({ lines: { height: px(88), backgroundColor: color.background, borderLeftWidth: size.border, borderRightWidth: size.border, borderBottomWidth: size.border, borderColor: color.border, // 子元素橫向排列,垂直居中,水平分佈,中間用空格填滿,最兩邊元素各自靠邊 ...layout.flex.vchbs, }, });
在個人項目中默認邊框的大小就是一個像素(1px),那麼只要在最外層聲明瞭 size.border
的大小,後面寫代碼的時候就能夠暢行無阻的書寫下去了,其實咱們已經模塊化了,只是咱們還不夠完全,不完全就表明着咱們的代碼不完美,並且可複用性差,就如上面的demo,若是咱們這裏須要一個三面的邊框,那麼其它組件需不須要呢?若是須要的話是否是也能夠像我這樣寫呢?
固然是不能夠!爲何?由於咱們是在複用這個邊框,因此咱們就不應再寫一份如出一轍的代碼了,而是應該寫相似這樣的:
export default StyleSheet.create({ lines: { height: px(88), backgroundColor: color.background, // 一個邊框粗細爲1px的紅色邊框 ...layout.border // 子元素橫向排列,垂直居中,水平分佈,中間用空格填滿,最兩邊元素各自靠邊 ...layout.flex.vchbs, }, });
這樣咱們的代碼不只少了不少,結構也清楚了,並且到時候替換或者修改的時候也容易一些了,不過寫成這樣就結束了嘛?固然不是了,咱們如今有一個紅色的邊框,因此咱們在layout模塊下新增了一個border屬性,那麼若是咱們有一個藍色的邊框呢?一個綠色的粗邊框呢?咱們會一直往layout模塊上新增屬性嘛?那最後你知道layout上面究竟有多少屬性嘛?那不就又回到一開始了嘛。。。
因此,個人建議是,處於根節點的模塊最好控制在3個左右:
backgroundColor: '#fff'
這樣的代碼了。那麼第二級中的屬性我也建議控制在5個左右:
這樣雖然增長了深度,可是分類清晰,結構明確,複用性也比較高。雖然可能會增長項目新建時的成本(建立各類分類),可是會給後續的開發、遷移、重構、複用等帶來極大的便捷。但這就結束了嘛?有的同窗和我說,我有不少的邊框啊,我有不少樣式要複用啊,到最後個人layout也會大到看不懂啊。。。還有的同窗說我沒有那麼多可複用的樣式啊,那是否是你總結的思路就用不上了啊。固然不是咯,咱們只完成了樣式模塊化的第一步(抽離樣式),接下來開始第二步。
如今不少web開發者在書寫css的時候已經再也不去寫原生的css了吧,而是採用例如scss、less這樣的預編譯語言去寫樣式了,那麼這些預編譯語言給咱們帶來了哪些方便呢?我想大多數同窗第一時間都會想到Mixin。
利用混合器,能夠很容易地在樣式表的不一樣地方共享樣式。若是你發現本身在不停地重複一段樣式,那就應該把這段樣式構形成優良的混合器,尤爲是這段樣式自己就是一個邏輯單元,好比說是一組放在一塊兒有意義的屬性。
在react-native上面,咱們的樣式代碼是js代碼,因此很自然的就自帶預編譯,不須要其它額外的語言去處理它,要作的只是判斷你的屬性是否須要一個Mixin。
判斷一組屬性是否應該組合成一個混合器,一條經驗法則就是你可否爲這個混合器想出一個好的名字。若是你能找到一個很好的短名字來描述這些屬性修飾的樣式,好比rounded-cornersfancy-font或者no-bullets,那麼每每可以構造一個合適的混合器。若是你找不到,這時候構造一個混合器可能並不合適。
那麼在js上面,我該如何實現一個Mixin呢?太簡單了!咱們只須要一個函數就能夠了,沒錯,只須要一個返回對象的函數就能夠作到這樣的效果了,加上ES7的拓展運算符,咱們就能夠作到一個混合器的效果:
export default StyleSheet.create({ lines: { height: px(88), backgroundColor: color.background, ...layout.border(1px, '#fff') }, });
常寫react-native的同窗必定都頭疼過這樣一個問題吧,就是咱們並不能像寫css樣式同樣在一行中把全部的屬性都寫完,在css中咱們若是想要聲明一個四面邊框的大小,能夠這樣寫:
.border { border: 10px 5px 10px 5px; }
那麼在咱們寫樣式的時候是否是也能夠這樣寫:
export default StyleSheet.create({ lines: { height: px(88), backgroundColor: color.background, ...layout.border(10px, 5px, 10px, 5px), }, });
咱們能夠經過函數的不一樣數量的參數來模擬傳統css開發的簡寫屬性,不少時候咱們更習慣在View上面去作樣式的運算,利用react-native樣式的覆蓋數組去不斷的覆蓋以前的樣式來達到運算的結果,這就致使View中除了須要計算你的組件要不要展現、如何展現以外,還要去計算樣式該如何寫,既然咱們要作樣式和頁面的分離,那就應該作完全一些,將樣式的計算也放在style.js中。
最後總結一下咱們所作的:
我建議不管你的項目多大,代碼多少,前三步都應該是一個必備的環節,可能你的項目不復雜,暫時用不到第四點,但前三條不管如何都應該儘早的去完善,這不只僅能幫助你實現後續的迭代,也能在你的腦中保留出一個對於項目完整結構的印象,要知道樣式是寄生於頁面的,清楚了樣式,那麼頁面如何你也多少會爛熟於心了。而相比於經過梳理js的邏輯去了解整個項目,我想經過頁面也許會更快吧,這對剛剛接手項目的新同窗來講,是很是友善的。
通常到這裏,就該放上本身開源的項目地址或者安利一波做者寫的庫了,不過和上一篇同樣,這裏咱們只討論思路,表述想法,而具體的實踐和代碼仍是要靠咱們本身在項目中不斷的總結和積累~
我相信不少同窗對於我提到的前三點都會很快的理解,而對於第四點可能就有些懵了,該怎麼去理解這個混合器呢?我該怎麼用js去實現一個呢?下面我就用一段代碼來舉個例子,該如何實現一個Mixin:
const layout = { // 這裏的形參順序遵循css中的 「上、右、下、左」 margin(...arg) { let margin = {}; switch (arg.length) { case 1: margin = { marginTop: arg[0], marginRight: arg[0], marginBottom: arg[0], marginLeft: arg[0], }; break; case 2: margin = { marginVertical: arg[0], marginHorizontal: arg[1], }; break; case 3: margin = { marginTop: arg[0], marginHorizontal: arg[1], marginBottom: arg[2], }; break; case 4: margin = { marginTop: arg[0], marginRight: arg[1], marginBottom: arg[2], marginLeft: arg[3], }; break; default: break; } return margin; }, };
這是一個最簡易的Mixin,你能夠根據你的需求去寫更多這樣的Mixin,其實我我的以爲在項目一開始的時候是不必定須要這個的,這個存在的意義是對於複雜樣式書寫的,更多的狀況下,你的項目只要作到了前三點,在樣式這一塊就已經很是的整潔、完善了,多數狀況下你不須要Mixin就能組織好你的代碼。
好了,以上就是此次我想和你們聊的關於react-native中樣式的話題了,咱們下次見~