玩轉 Taro 跨端之 flex 佈局篇

文內項目地址:github.com/NervJS/taro…css

Taro 是一套遵循 React 語法規範的跨平臺開發解決方案,可是目前當咱們使用 Taro 的時候,在不一樣平臺上的開發體驗還有不一致的地方,因此咱們也都期待有一套跨平臺統一的解決方案,可以以最小差別的方式向開發者提供更好的開發體驗。前端

在跨平臺開發的過程當中,不一樣平臺之間的差別尤爲體如今樣式的統一上,因爲不一樣平臺對樣式的支持程度並不一致,Taro 很難可以經過編譯的手段來對跨平臺樣式進行統一,因此,咱們須要一個支持跨平臺的樣式解決方案來對其進行統一。react

跨平臺樣式

考慮頁面佈局和樣式 H5 是最爲靈活的,小程序次之,React Native 和快應用最弱,統一跨平臺樣式應當優先對齊短板,也就是要以 React Native 和快應用的約束來管理樣式,同時兼顧小程序的限制,而 Flexbox 就是一個很好的樣式解決方案。git

在構建頁面的時候,咱們能夠經過 Flexbox 高效地完成頁面代碼,雖然並非全部屬性均可以全平臺適應的,可是它在全平臺都可以獲得足夠的支持,並且全部平臺能夠很容易經過它來繪製通用性很高的頁面,這也就是爲何咱們選擇使用 Flexbox 方案來完成這個跨平臺演示項目。github

taro flexbox 效果圖

預覽地址web

Flexbox 佈局 (Flexible Box Layout)

Flexbox 是彈性佈局模塊(CSS Flexible Box Layout Module)經常使用的簡稱,是一種用於在單個維度中顯示項目行或列的佈局模型。在規範中, Flexbox 被描述爲用戶界面設計的佈局模型。 Flexbox 的關鍵特性是 flex 佈局中的項目能夠增加和縮小。能夠將空間分配給項目自己,或者在項目之間或周圍分配空間。小程序

在 Flexbox 中,採用 flex 佈局 的元素,稱爲 flex 容器(flex container), flex 容器全部的子元素自動成爲容器成員,稱爲 flex 元素(flex item)。Flex 容器 默認存在兩根軸:水平的主軸(main-axis)和垂直的交叉軸(cross-axis)。flex 元素 默認沿主軸排列。主軸的開始位置(與邊框的交叉點)叫作 main-start ,結束位置叫作 main end ;交叉軸的開始位置叫作 cross-start ,結束位置叫作 cross-end ;單個項目佔據的主軸空間叫作 main-size ,佔據的交叉軸空間叫作 cross-size微信小程序

flex-direction-terms

Flexbox 能夠對齊主軸或橫軸上的項目,從而提供對一組項目的大小和對齊的高級控制,大多數場景下,使用 flex-direction、align-items 和 justify-content 三個樣式屬性就已經能知足大多數佈局需求,換而言之若是熟悉 Flexbox 就能夠應對大多數場景下的佈局需求。react-native

注意,設爲 Flex 佈局之後,子元素的 floatclearvertical-align 屬性將失效。瀏覽器

Flex Container 屬性

在規範中, Flex Container 上,一共有七個屬性能夠設置,可是 flex-flow 在 React Native 上是不支持的。

flex-direction

flex-direction 屬性指定了flex 元素是如何在 flex 容器中佈局的,定義了主軸的方向(正方向或反方向)。

支持的值以下:

意義
row flex 容器的主軸被定義爲與文本方向相同。 主軸起點和主軸終點與內容方向相同。
row-reverse 表現和 row 相同,可是置換了主軸起點和主軸終點。
column flex 容器的主軸和塊軸相同。主軸起點與主軸終點和書寫模式的先後點相同。
column-reverse 表現和 column 相同,可是置換了主軸起點和主軸終點。

須要注意的是,規範下 flex-direction 的默認值是 row ,而在 React Native 中則爲 column,這也就是爲何咱們會添加了這個的樣式

.flex {
    display: flex;
    flex-direction: row;
}
複製代碼

flex-wrap

flex-wrap 指定 flex 元素單行顯示仍是多行顯示。若是容許換行,這個屬性容許控制行的堆疊方向。默認值爲 nowrap。

支持的值以下:

意義
nowrap 不換行。flex 元素被擺放到到一行,這可能致使溢出 flex 容器。交叉軸的起點會根據 flex-direction 的值至關於 start 或 before。
wrap flex 元素被打斷到多個行中。交叉軸的起點會根據 flex-direction 的值選擇等於start 或before。交叉軸的終點爲肯定的交叉軸的起點的另外一端。
wrap-reverse 和 wrap 的行爲同樣,可是交叉軸的起點和交叉軸的終點互換。

使用 flex-wrap 屬性的時候,咱們須要注意 wrap-reverse 值在 React Native 上是不支持的。

flex-flow

flex-flow 屬性是 flex-direction 和 flex-wrap 的簡寫。默認值爲 row nowrap。

語法格式

<'flex-direction'> || <'flex-wrap'>
複製代碼

flex-flow 屬性不被 React Native 和快應用支持

align-items

align-items 屬性將全部直接子節點上的 align-self 值設置爲一個組。 align-self 屬性設置項目在其包含塊中在交叉軸方向上的對齊方式。默認值爲 stretch。

意義
stretch flex 元素在交叉軸方向拉伸到與容器相同的高度或寬度(flex 元素不能固定尺寸)
flex-start 交叉軸的起點對齊
flex-end 交叉軸的終點對齊
center 交叉軸的中點對齊
baseline 元素第一行文字的基線對齊

語法格式

normal | stretch | <baseline-position> | [ <overflow-position>? <self-position> ]
where 
<baseline-position> = [ first | last ]? baseline
<overflow-position> = unsafe | safe
<self-position> = center | start | end | self-start | self-end | flex-start | flex-end
複製代碼

baseline 值不被 React Native 和快應用支持

space-evenly、start、end、self-start、self-end、left、right、first baseline、last baseline、safe、unsafe 在 flex 佈局中通用性低

align-content

align-content 屬性設置瞭如何沿着 flex 容器的交叉軸和在 flex 元素之間和周圍分配空間。默認值爲 stretch。

該屬性對單行彈性盒子模型無效。(即:帶有 flex-wrap: nowrap 的 flex 容器)。

意義
stretch 拉伸全部 flex 元素來填滿剩餘空間。剩餘空間平均的分配給每個 flex 元素
flex-start 全部 flex 元素從垂直軸起點開始填充。第一個 flex 元素的垂直軸起點邊和 flex 容器的垂直軸起點邊對齊。接下來的每個 flex 元素緊跟前一個 flex 元素。
flex-end 全部 flex 元素從垂直軸末尾開始填充。最後一個 flex 元素的垂直軸終點和容器的垂直軸終點對齊。同時全部後續 flex 元素與前一個對齊。
center 全部 flex 元素朝向容器的中心填充。每 flex 元素互相緊挨,相對於容器居中對齊。容器的垂直軸起點邊和第一個 flex 元素的距離相等於容器的垂直軸終點邊和最後一個 flex 元素的距離。
space-between 全部 flex 元素在容器中平均分佈。相鄰兩 flex 元素間距相等。容器的垂直軸起點邊和終點邊分別與第一個 flex 元素和最後一個 flex 元素的邊對齊。
space-around 全部 flex 元素在 flex 容器中平均分佈,相鄰兩 flex 元素間距相等。容器的垂直軸起點邊和終點邊分別與第一個 flex 元素和最後一個 flex 元素的距離是相鄰兩 flex 元素間距的一半。
space-evenly flex 元素都沿着主軸均勻分佈在指定的 flex 元素中。相鄰 flex 元素之間的間距,主軸起始位置到第一個 flex 元素的間距,,主軸結束位置到最後一個 flex 元素的間距,都徹底同樣。

語法格式

normal | <baseline-position> | <content-distribution> | <overflow-position>? <content-position>
where 
<baseline-position> = [ first | last ]? baseline
<content-distribution> = space-between | space-around | space-evenly | stretch
<overflow-position> = unsafe | safe
<content-position> = center | start | end | flex-start | flex-end
複製代碼

React Native 中須要版本號在 0.58 以上

且 flex-wrap 屬性值須要爲 wrap

同時只對橫軸生效(即 flex-direction 屬性爲 column 或 column-reverse)

baseline 值不被 React Native 支持

space-evenly、start、end、left、right、first baseline、last baseline、safe、unsafe 在 flex 佈局中通用性低

justify-content

justify-content 屬性定義了瀏覽器如何分配順着 flex 容器主軸的 flex 元素之間及其周圍的空間。

意義
flex-start 從行首開始排列。每行第一個 flex 元素與行首對齊,同時全部後續的 flex 元素與前一個對齊。
flex-end 從行尾開始排列。每行最後一個 flex 元素與行尾對齊,其餘元素將與後一個對齊。
center 伸縮元素向每行中點排列。每行第一個元素到行首的距離將與每行最後一個元素到行尾的距離相同。
space-between 在每行上均勻分配 flex 元素。相鄰元素間距離相同。每行第一個元素與行首對齊,每行最後一個元素與行尾對齊。
space-around 在每行上均勻分配 flex 元素。相鄰元素間距離相同。每行第一個元素到行首的距離和每行最後一個元素到行尾的距離將會是相鄰元素之間距離的一半。
space-evenly flex 元素都沿着主軸均勻分佈在指定的 flex 元素中。相鄰 flex 元素之間的間距,主軸起始位置到第一個 flex 元素的間距,,主軸結束位置到最後一個 flex 元素的間距,都徹底同樣。

語法格式

normal | <content-distribution> | <overflow-position>? [ <content-position> | left | right ]
where 
<content-distribution> = space-between | space-around | space-evenly | stretch
<overflow-position> = unsafe | safe
<content-position> = center | start | end | flex-start | flex-end
複製代碼

baseline 值不被 React Native 支持

stretch、space-evenly、start、end、left、right、first baseline、last baseline、safe、unsafe 在 flex 佈局中通用性低

place-content

place-content 屬性是 align-content 和 justify-content 的簡寫。

語法格式

<'align-content'> <'justify-content'>?
複製代碼
  • 若是第二個值不存在,且第一個值適用於用於二者,則第二個值複用第一個
  • 若是第二個值不存在,且第一個值不適用於用於二者,則整個值無效

place-content 屬性不被 React Native 支持

Flex Item 屬性

在 Flex Item 上,一樣也有六個屬性,而 order 屬性在 React Native 上不支持。

order

order 屬性規定了 flex 容器中的 flex 元素在佈局時的順序。flex 元素按照 order 屬性的值的增序進行佈局。擁有相同 order 屬性值的 flex 元素按照它們在源代碼中出現的順序進行佈局。默認值爲 0。

語法格式

<integer>
複製代碼

order 屬性不被 React Native 支持

flex-grow

flex-grow 屬性定義 flex 元素的拉伸因子。

語法格式

<number> | inherit
複製代碼

負值無效

React Native 上默認值爲 0

flex-shrink

flex-shrink 屬性指定了 flex 元素的收縮規則。flex 元素僅在默認寬度之和大於容器的時候纔會發生收縮,其收縮的大小是依據 flex-shrink 的值。默認值爲 1。

語法格式

<number> | inherit
複製代碼

負值是不被容許的。

React Native 上默認值爲 1

flex-basis

flex-basis 指定了 flex 元素在主軸方向上的初始大小。若是不使用 box-sizing 改變盒模型的話,那麼這個屬性就決定了 flex 元素的內容盒(content-box)的尺寸。

注意:若是一個 flex 元素同時設置了 flex-basis (auto 除外)和 width (或者 flex-direction: column 時設置了 height ),flex-basis 權級更高。

語法規範

content | <'width'>
複製代碼
  • <'width'>
    • width 值能夠是<length>;
    • 該值也能夠是一個相對於其父彈性盒容器主軸尺寸的百分數 。
    • 負值是不被容許的。
    • 默認爲 0。
  • content
    • 基於 flex 元素的內容自動調整大小。

React Native 上使用 ScrollView 組件會致使屬性失效

若是沒有足夠空間,組件不會發生收縮 (應該是設置了 flex-shrink 屬性值默認爲 0)

flex

flex 規定了 flex 元素如何伸長或縮短以適應 flex 容器中的可用空間。這是一個簡寫屬性,用來設置 flex-grow, flex-shrink 與 flex-basis。

語法格式

none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
複製代碼
  • initial
    • 元素會根據自身寬高設置尺寸。
    • 它會縮短自身以適應 flex 容器,但不會伸長並吸取 flex 容器中的額外自由空間來適應 flex 容器 。
    • 至關於將屬性設置爲"flex: 0 1 auto"。
  • auto
    • 元素會根據自身的寬度與高度來肯定尺寸,可是會伸長並吸取 flex 容器中額外的自由空間,也會縮短自身來適應 flex 容器。
    • 這至關於將屬性設置爲 "flex: 1 1 auto"。
  • none
    • 元素會根據自身寬高來設置尺寸。
    • 它是徹底非彈性的:既不會縮短,也不會伸長來適應 flex 容器。
    • 至關於將屬性設置爲"flex: 0 0 auto"。

在 React Native 中只能爲 number 類型

  • 當 flex > 0 時,組件大小將與其彈性值成比例。所以,flex 設置爲 2 的組件將佔用空間的兩倍做爲 flex 設置爲 1 的組件
  • 當 flex = 0 時,組件根據 width 和 height 肯定大小,且不會發生變化。
  • 當 flex = -1 時,組件一般根據 width 和 height 肯定大小。可是,若是沒有足夠的空間,組件將收縮到 minWidth 和 minHeight。

在快應用中,flex 的快捷值設置均是無效值

align-self

align-self 會對齊當前 flex 行中的 flex 元素,並覆蓋 align-items 的值. 若是任何 flex 元素的側軸方向 margin 值設置爲 auto,則會忽略 align-self。

語法格式

auto | normal | stretch | <baseline-position> | <overflow-position>? <self-position>
where 
<baseline-position> = [ first | last ]? baseline
<overflow-position> = unsafe | safe
<self-position> = center | start | end | self-start | self-end | flex-start | flex-end
複製代碼

baseline 值不被 React Native 和快應用支持

start、end、self-start、self-end、first baseline、last baseline、safe、unsafe 在 flex 佈局中通用性低

組件化開發

不一樣的平臺如 Web、React-Native、微信小程序等各有特點,平臺之間的差別很大,會致使不少額外的開發成本。那麼若是咱們想要完成一個跨平臺項目該怎麼作呢?

咱們開始從比較容易入手的方向考慮,若是採用模塊化組件或是 css-in-js 的方案去完成樣式的構建會是一個好的方案麼?

在目前的前端生態中,模塊化組件開發會是個很棒的方案,覆蓋模式下構建開箱即用的組件同時能夠提供方法來覆蓋樣式再好不過了,可是若是放到小程序開發的模式中,這就會有個很嚴重的問題,那就是若是咱們在層級樣式表中寫到的樣式,是不能直接傳給組件來覆蓋樣式的,組件和組件的隔離在不一樣小程序中很難被打破。

/* CustomComp.js */
export default class CustomComp extends Component {
  static defaultProps = {
    className: ''
  }

  render () {
    return <View className={this.props.className}>這段文本的顏色不會由組件外的 class 決定</View>
  }
}

/* MyPage.js */
export default class MyPage extends Component {
  render () {
    return <CustomComp className="red-text" /> } } 複製代碼
/* MyPage.scss */
.red-text {
  color: red;
}
複製代碼

若是你們嘗試上述的寫法,會發現 red-text 類中的樣式並無生效,那麼在這種狀況下咱們若是考慮是使用 css-in-js 會好麼?很遺憾,若是你使用它,咱們將不會爲這些須要運行時處理的樣式補全前綴。

這兩個方案都不是合適的方案,那麼咱們該怎麼作呢?試着去打破小程序的組件限制麼?咱們在微信小程序官方的文檔中找到 externalClasses 這個方法,能夠先來嘗試。

/* CustomComp.js */
export default class CustomComp extends Component {
  static externalClasses = ['my-class']

  render () {
    return <View className="my-class">這段文本的顏色由組件外的 class 決定</View>
  }
}

/* MyPage.js */
export default class MyPage extends Component {
  render () {
    return <CustomComp className="red-text" /> } } 複製代碼
/* MyPage.scss */
.red-text {
  color: red;
}
複製代碼

可是這也並不是全部的開發平臺都可以提供給開發者相關的方法,因此咱們只能轉換目光到另外一個 addGlobalClass 方法上,這個方法不只在全部小程序都可以支持,Taro 在 React Native 端上也提供了一樣的方法給你們,這樣咱們也能夠避開 css modules 這個體驗稍差的方法。

/* CustomComp.js */
export default class CustomComp extends Component {
  static options = {
    addGlobalClass: true
  }

  render () {
    return <View className="red-text">這段文本的顏色由組件外的 class 決定</View>
  }
}
複製代碼
/* 組件外的樣式定義 */
.red-text {
  color: red;
}
複製代碼

寫在最後

在項目中,咱們已經將全部通用支持的方法寫到 scss 文件中,若是你們須要能夠直接使用咱們已經提供的 flexbox 樣式,按以下方法在本身全局的層級樣式表中引入咱們已經提供的樣式。

@import 'https://raw.githubusercontent.com/NervJS/taro-flexbox/master/flexbox-demo/src/asset/flex.scss';
複製代碼

那麼關於 Flex 佈局的知識,若是文中有遺漏的,你們能夠跟着咱們的項目來梳理知識,也能夠到 MDN 上查看相關的文檔,值得注意的是在 Flexbox 佈局中 gap、row-gap、column-gap 屬性在 Grid 佈局中廣泛支持,可是在 Flex 佈局中卻只有 Firefox 完成了適配,因此暫且不表,

一樣 justify-content 屬性的 space-evenly 值在 web 端通用性很低,不建議使用。

gap、row-gap、column-gap in flex

gap_in_flex

space-evently on desktop

space-evently-desktop

space-evently on mobile

space-evently-mobile

但願這篇文章能夠爲你提供一些幫助。

屬性對照表

屬性 Chrome Firefox Android webview Safari on iOS 微信小程序 支付寶小程序 百度小程序 頭條小程序 QQ小程序 React Native
flex-direction 部分差別
flex-wrap wrap-reverse 不支持
flex-flow 不支持
align-items baseline 不支持
align-content 0.58+ (部分差別)
justify-content baseline 不支持
place-content 不支持
order 不支持
flex-grow
flex-shrink
flex-basis 部分差別
flex 語法不一樣
align-self baseline 不支持

相關連接

相關文章
相關標籤/搜索