個人前端故事----來聊聊怎麼寫react-native上的樣式吧

我遇到了什麼問題?

不久以前我重構了一個古老的項目,總結了一些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個左右:

  • color:用於存放整個項目的所有顏色,這也表明着,在組件的style內部,咱們不該該再顯示的書寫諸如backgroundColor: '#fff'這樣的代碼了。
  • size:用於存放整個項目的通用大小,好比說行高、間距、字體大小等公共的數值參數。
  • layout:用於存放整個項目的公共佈局,例如控制佈局的flex屬性、通用的padding、margin、position定位。

那麼第二級中的屬性我也建議控制在5個左右:

  • 顏色:邊框顏色、背景顏色、字體顏色。。。
  • 大小:邊框大小、間距大小、字體大小。。。
  • 佈局:flex佈局、position定位。。。

這樣雖然增長了深度,可是分類清晰,結構明確,複用性也比較高。雖然可能會增長項目新建時的成本(建立各類分類),可是會給後續的開發、遷移、重構、複用等帶來極大的便捷。但這就結束了嘛?有的同窗和我說,我有不少的邊框啊,我有不少樣式要複用啊,到最後個人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中樣式的話題了,咱們下次見~

相關文章
相關標籤/搜索