Taro 是一套遵循 React 语法规范的跨平台开发解决方案,但是目前当我们使用 Taro 的时候,在不同平台上的开发体验还有不一致的地方,所以我们也都期待有一套跨平台统一的解决方案,能够以最小差异的方式向开发者提供更好的开发体验。

在跨平台开发的过程中,不同平台之间的差异尤其体现在样式的统一上,由于不同平台对样式的支持程度并不一致,Taro 很难能够通过编译的手段来对跨平台样式进行统一,所以,我们需要一个支持跨平台的样式解决方案来对其进行统一。

跨平台样式

考虑页面布局和样式 H5 是最为灵活的,小程序次之,React Native 和快应用最弱,统一跨平台样式应当优先对齐短板,也就是要以 React Native 和快应用的约束来管理样式,同时兼顾小程序的限制,而 Flexbox 就是一个很好的样式解决方案。

在构建页面的时候,我们可以通过 Flexbox 高效地完成页面代码,虽然并不是所有属性都可以全平台适应的,但是它在全平台都能够得到足够的支持,而且所有平台可以很容易通过它来绘制通用性很高的页面,这也就是为什么我们选择使用 Flexbox 方案来完成这个跨平台演示项目。

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 。

Flexbox 可以对齐主轴或横轴上的项目,从而提供对一组项目的大小和对齐的高级控制,大多数场景下,使用 flex-direction、align-items 和 justify-content 三个样式属性就已经能满足大多数布局需求,换而言之如果熟悉 Flexbox 就可以应对大多数场景下的布局需求。

注意,设为 Flex 布局以后,子元素的 float 、 clear 和 vertical-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 属性是 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 这个方法,可以先来尝试。

不同的平台如 Web、React-Native、微信小程序等各有特色,平台之间的差异很大,会导致很多额外的开发成本。那么如果我们想要完成一个跨平台项目该怎么做呢?

/* 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[1] 上查看相关的文档,值得注意的是在 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

属性对照表

项目地址

项目地址:

https://github.com/NervJS/taro-flexbox

预览地址:

https://nervjs.github.io/taro-flexbox/

预览地址也可以点击文章末尾的「阅读原文」体验 H5 版的效果

相关链接

  • 预览地址:https://nervjs.github.io/taro-flexbox/

  • 项目地址:https://github.com/NervJS/taro-flexbox

  • 参考资料:

    • https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout#Reference

    • https://facebook.github.io/react-native/docs/flexbox

    • https://github.com/startheart/cml-flexbox 2

    • [1]MDN: https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Flexible_Box_Layout#%E5%8F%82%E8%80%83

原创系列推荐



4. 
5. 
6. 
7. 

回复“加群”与大佬们一起交流学习~

点这,与大家一起分享本文吧~

【Taro】363- 玩转 Taro 跨端之 flex 布局篇的更多相关文章

  1. 移动端自适应rem 布局篇

    相信很多刚开始写移动端页面的同学都要面对页面自适应的问题,当然解决方案很多,比如:百分比布局,弹性布局flex(什么是flex),也都能获得不错的效果,这里主要介绍的是本人在实践中用的最顺手最简单的布 ...

  2. 移动端display:flex

    移动端display:flex布局时候,子元素有背景颜色时候,背景颜色不能铺满,有缝隙, // less .t-flex { background: blue; display: flex; > ...

  3. Taro 3 正式版发布:开放式跨端跨框架解决方案

    作者:凹凸曼 - yuche 从 Taro 第一个版本发布到现在,Taro 已经接受了来自于开源社区两年多的考验.今天我们很高兴地在党的生日发布 Taro 3(Taro Next)正式版,希望 Tar ...

  4. 用 React 编写的基于Taro + Dva构建的适配不同端(微信小程序、H5、React-Native 等)的时装衣橱

    前言 Taro 是一套遵循 React 语法规范的 多端开发 解决方案.现如今市面上端的形态多种多样,Web.React-Native.微信小程序等各种端大行其道,当业务要求同时在不同的端都要求有所表 ...

  5. Taro聊天室|react+taro仿微信聊天App界面|taro聊天实例

    一.项目简述 taro-chatroom是基于Taro多端实例聊天项目,运用Taro+react+react-redux+taroPop+react-native等技术开发的仿微信App界面聊天室,实 ...

  6. 重磅!滴滴跨端框架Chameleon 1.0正式发布

    滴滴在 GitHub 上开源的跨端解决方案 Chameleon(简写 CML)正式发布 1.0 版本,中文名卡梅龙:中文意思变色龙,意味着就像变色龙一样能适应不同环境的企业级跨端整体解决方案,具有易用 ...

  7. React / Vue 跨端渲染原理与实现探讨

    跨端渲染是渲染层并不局限在浏览器 DOM 和移动端的原生 UI 控件,连静态文件乃至虚拟现实等环境,都可以是你的渲染层.这并不只是个美好的愿景,在今天,除了 React 社区到 .docx / .pd ...

  8. HarmonyOS新能力让数据多端协同更便捷,数据跨端迁移更高效!

    作者:yijian,终端OS分布式文件系统专家:gongashi,终端OS分布式数据管理专家 HarmonyOS作为分布式操作系统,其分布式数据管理能力非常重要.我们也一直围绕持续为开发者带来全局&q ...

  9. Flutter ChartSpace:通过跨端 Canvas 实现图表库

    基于Flutter 的图形语法库,通过跨端 Canvas ,将基于 Javascript 的图形语法库 ChartSpace 扩展至 Flutter 端 作者:字节跳动终端技术--胡珀 背景 数据平台 ...

随机推荐

  1. 简单说说基于JWT的token认证方式

    一.什么是认证 好多人不知道什么是认证,认证,其实就是服务端确认用户身份.Http协议是无状态的,客户端发送一条请求,服务端返回一条响应,二者就算做成一单买卖,一拍两散.在很久以前,互联网所能提供的服 ...

  2. 元数据管理的重要性 - xms

    什么是元数据?引用百科的描述就是:元数据(Metadata),又称中介数据.中继数据,为描述数据的数据(data about data),主要是描述数据属性(property)的信息: 看起来有点抽象 ...

  3. 研究Java语言的编译器和虚拟机源代码

    现在使用Java语言的人很多,但是了解Java语言实现的人非常少.如果要研究Java语言的实现,推荐研究Javac和虚拟机HotSpot的源代码实现,其中Javac相当于Java编译的前端,HotSp ...

  4. 关于C# 语言

    C# 语法高度重视表达,但学习起来也很简单轻松. 任何熟悉 C.C++ 或 Java 的人都可以立即认出 C# 的大括号语法. 通常情况下,了解上述任何一种语言的开发者可以在很短的时间内就开始使用 C ...

  5. [CSS七分钟系列]都1902年了,还不知道用margin:auto给flex容器内元素分组?

    最近看到几篇博文讲解margin:auto在flex容器中的使用,可惜的是大多讲解都浮于页面表现,没深究其中的作用机理,本文在此浅薄对其表现机理做简单探讨. 引子 日常业务迭代过程中,flex已经是前 ...

  6. 解决配置vim中文乱码的问题

    解决linux下vim乱码的情况:(修改vimrc的内容) 全局的情况下:即所有用户都能用这个配置 文件地址:/etc/vimrc 在文件中添加: set fileencodings=utf-8,uc ...

  7. Streams:深入理解Redis5.0新特性

    概述 相较于Redis4.0,Redis5.0增加了很多新的特性,而streams是其中最重要的特性之一.streams是redis 的一种基本数据结构,它是一个新的强大的支持多播的可持久化的消息队列 ...

  8. 【Android - 控件】之V - Toolbar的使用

    Toolbar是Android V7包中的一个控件,用来代替Action Bar作为界面的头部标题栏布局.Toolbar相对于Action Bar的特点是更加灵活,可以显示在任何位置. 首先先来看To ...

  9. R 语言学习笔记(3)—— 基础绘图

    R 中图形的概念 在 R 中图,就像 photoshop 中的图层一样,每一个元素都是层层向上延展构建的,最终形成了我们视觉上所形成的平面图形.这些元素包含了常见的图形的标题(title).坐标轴(a ...

  10. 【w、vmstat、top、sar、nload】各个命令 使用介绍

    第7周第1次课(5月7日) 课程内容: 10.1 使用w查看系统负载10.2 vmstat命令10.3 top命令10.4 sar命令10.5 nload命令 10.1 使用w查看系统负载 w命令查看 ...