RN布局 & 适配
布局
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
flexis 0, the component is sized according towidthandheight, and it is inflexible.
但是实际看此时flexBasis要设置为auto,更像是flexGrow: 0, flexShink: 1, flexBasis: 0。即flex: 0跟flex: 正数的效果是一样的。
-
When
flexis -1, the component is normally sized according towidthandheight. However, if there's not enough space, the component will shrink to itsminWidthandminHeight
即先取值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
参考
适配
指定元素宽高
ReactNative有3种方式指定宽高尺寸:
- 绝对尺寸
- Flex方式
- 百分比方式
绝对尺寸
All dimensions in React Native are unitless, and represent density-independent pixels.
- 无单位
- 密度无关的像素,即逻辑像素(设备独立像素)
逻辑像素(dp)
简单回顾下逻辑像素概念:同样的尺寸在不同的设备(低分辨率和高分辨率)上看起来是“一样的”。
无单位
iOS和Android平台里逻辑像素单位是不同的,ReactNative是作为夸平台技术直接采用无单位方式表示逻辑像素。
App适配
参考
定义样式
- 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.freezestyle对象的属性,不要直接修改StyleSheet.create返回值的属性。