RN布局 & 适配
yaofly2012 opened this issue · comments
布局
React Native有两种布局方式:
- 控制子元素展示的布局:
flex
布局方式; - 控制自身相对应父元素展示的布局:
relative
/absolute
布局方式。
relative
/absolute
布局
相对于WEB CSS的position
属性,React Native里position
属性只有像个值:
relative
默认值absolute
并且含义跟WEB CSS的position
属性一样的。由于其默认值是relative
,这导致absolute
元素永远相对于其父元素位置。
Flex
布局
子元素的布局有且只有一种布局方式,即flex
布局。所以WEB CSS不一样的是父组件不用指定是否开启flex
布局。
父组件相关属性
flexDirection
justifyContent
alignItems
alignContent
flexWrap
子组件相关属性
flex
根WEB CSS flex
不一样的是React Nativeflex
只能是单个数字值。
三种取值逻辑:
-
正数,当取值是正数时
flex
相当于是flexGrow: XXX, flexShrink: 1, flexBasis: 0
的简写形式;
如果flex: 1
相当于flexGrow: 1, flexShrink: 1, flexBasis: 0
。
最常用的场景就是flex: 1
使得容器占满整个空间。 -
When
flex
is 0, the component is sized according towidth
andheight
, and it is inflexible.
但是实际看此时flexBasis
要设置为auto
,更像是flexGrow: 0, flexShink: 1, flexBasis: 0
。即flex: 0
跟flex: 正数
的效果是一样的。
-
When
flex
is -1, the component is normally sized according towidth
andheight
. However, if there's not enough space, the component will shrink to itsminWidth
andminHeight
即先取值width
/height
如果没有足够空间再取值 minWidth
/minHeight
。
只要是负数都一样,一般给-1
。
Issues/Concern:
flex
默认值是什么?【没有值,即此时看flexShrink
,flexBasis
和flexGrow
的值了】items
和content
区别:
items
特指子组件content
表示父组件包含的内容,除了包含子组件外,还可能会有空白区域。
alignSelf
和CSS Flex语法差异
flex
属性取值差异;- 默认值差异(RN针对移动端)
flexDirection
默认值column
;alignContent
默认值flex-start
;flexShrink
默认值0(flexBasis
和flexGrow
默认值跟CSS Flex一致).
Row Gap, Column Gap and Gap
gap
, rowGap
, columnGap
用于设置子组件之间的缝隙。子组件和父组件边缘之间的缝隙并不受影响。
注意这是ReactNative 0.71才引入的新Style属性。之前的版本可以采用这种方式模拟gap
参考
定义样式
- RN中利用JS声明样式;
- RN核心组件都有个
style
属性,用于定义组件样式;并且可以传个对象数组用于实现样式层叠。 - 使用
StyleSheet
模块将组件的样式统一放在一个地方,可简化组件render函数,提高组件的可读性。
StyleSheet
API
StyleSheet
API模块除了包含常用的Util函数(比如StyleSheet.create
)外,还有一些常用属性:
StyleSheet.create
函数
大部分情况我们都是在组件外部使用StyleSheet.create
函数创建styles变量,并在组件内部引用样式。但是StyleSheet.create
函数呢,不使用StyleSheet.create
函数行不行呢?
不使用StyleSheet.create
函数创建样式行不行 ?
行,直接使用纯JS对象也可以声明样式。
const styles = {
bigblue: {
color: 'blue',
fontWeight: 'bold',
fontSize: 30,
},
red: {
color: 'red',
},
};
但是不要这样做,RN既然提供了StyleSheet.create
函数,肯定有充分的理由,后面我们深入了解下StyleSheet.create
函数。
为啥使用StyleSheet.create
函数
StyleSheet.create
函数的返回值就是传入的实参:
const stylesObj = {
bigblue: {
color: 'blue',
fontWeight: 'bold',
fontSize: 30,
},
red: {
color: 'red',
},
};
const styles = StyleSheet.create(stylesObj);
console.log(stylesObj === styles); // true
那为啥还要使用StyleSheet.create
函数呢?从v0.72的代码注释里可以看出主要基于两个目的:
- 代码质量
- By moving styles away from the render function, you're making the code easier to understand
- Naming the styles is a good way to add meaning to the low level components in the render function
- 性能
- Making a stylesheet from a style object makes it possible to refer to it by ID instead of creating a new style object every time.
- It also allows to send the style only once through the bridge. All subsequent uses are going to refer an id (not implemented yet).
但是从目前实现看只有代码质量这个动机了:
StyleSheet.create
会校验实参是否是合法的Style属性(但是从v0.66开始不会再校验了,而是利用TS声明方式提示);- 借助TS类型声明,在coding时编译器会有智能提示。
StyleSheet.create
函数源码解析
StyleSheet.create
函数实现一直在变更,并且到目前(2023-07-16)还未完全实现(见上面注释)。我们看下v0.64~0.65和0.66~0.72两个阶段的版本:
create<+S: ____Styles_Internal>(
obj: S,
): $ObjMap<S, (Object) => StyleSheetInternalStyleIdentifier> {
const result = {};
for (const key in obj) {
StyleSheetValidation.validateStyle(key, obj);
result[key] = obj[key] && ReactNativePropRegistry.register(obj[key]);
}
return result;
}
create<+S: ____Styles_Internal>(obj: S): $ReadOnly<S> {
// TODO: This should return S as the return type. But first,
// we need to codemod all the callsites that are typing this
// return value as a number (even though it was opaque).
if (__DEV__) {
for (const key in obj) {
StyleSheetValidation.validateStyle(key, obj);
if (obj[key]) {
Object.freeze(obj[key]);
}
}
}
return obj;
}
create<+S: ____Styles_Internal>(obj: S): $ReadOnly<S> {
// TODO: This should return S as the return type. But first,
// we need to codemod all the callsites that are typing this
// return value as a number (even though it was opaque).
if (__DEV__) {
for (const key in obj) {
if (obj[key]) {
Object.freeze(obj[key]);
}
}
}
return obj;
}
可以看出随着版本的升级StyleSheet.create
函数功能反而更简化了,从v0.65开始(目前v0.72)只是原封不动的返回实参。
总结
综上所述:
- 从render函数中移除具体的样式内容,可以使代码更清晰易懂;
- 样式命名也可以对render函数中的组件增加语义化的描述
// 不推荐 ❌
<View style={{ backgroundColor: '#fff‘ }} />
// 推荐 ✅
const styles = StyleSheet.create({
wrapper: {
backgroundColor: '#fff‘
}
});
// 说明View作为wrapper
<View style={styles.wrapper} />
- 把
style
对象作为只读对象对待
开发阶段StyleSheet.create
函数会Object.freeze
style对象的属性,不要直接修改StyleSheet.create
返回值的属性。