yaofly2012 / note

Personal blog

Home Page:https://github.com/yaofly2012/note/issues

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

RN布局 & 适配

yaofly2012 opened this issue · comments

commented

布局

React Native有两种布局方式:

  1. 控制子元素展示的布局:flex布局方式;
  2. 控制自身相对应父元素展示的布局:relative/absolute布局方式。

relative/absolute布局

相对于WEB CSS的position属性,React Native里position属性只有像个值:

  1. relative默认值
  2. absolute

并且含义跟WEB CSS的position属性一样的。由于其默认值是relative,这导致absolute元素永远相对于其父元素位置

Flex布局

子元素的布局有且只有一种布局方式,即flex布局。所以WEB CSS不一样的是父组件不用指定是否开启flex布局。

父组件相关属性

flexDirection

justifyContent

alignItems

alignContent

flexWrap

子组件相关属性

flex

根WEB CSS flex不一样的是React Nativeflex只能是单个数字值。
三种取值逻辑

  1. 正数,当取值是正数时flex相当于是flexGrow: XXX, flexShrink: 1, flexBasis: 0的简写形式;
    如果flex: 1 相当于flexGrow: 1, flexShrink: 1, flexBasis: 0
    最常用的场景就是flex: 1使得容器占满整个空间。

  2. When flex is 0, the component is sized according to width and height, and it is inflexible.

但是实际看此时flexBasis要设置为auto,更像是flexGrow: 0, flexShink: 1, flexBasis: 0。即flex: 0flex: 正数的效果是一样的。

  1. When flex is -1, the component is normally sized according to width and height. However, if there's not enough space, the component will shrink to its minWidth and minHeight

即先取值width/height如果没有足够空间再取值 minWidth/minHeight
只要是负数都一样,一般给-1

Issues/Concern:

  1. flex默认值是什么?【没有值,即此时看flexShrinkflexBasisflexGrow的值了】
  2. itemscontent区别:
  • items特指子组件
  • content表示父组件包含的内容,除了包含子组件外,还可能会有空白区域。

alignSelf

和CSS Flex语法差异

  1. flex属性取值差异;
  2. 默认值差异(RN针对移动端)
  • flexDirection默认值column;
  • alignContent默认值flex-start;
  • flexShrink默认值0(flexBasisflexGrow默认值跟CSS Flex一致).

Row Gap, Column Gap and Gap

gap, rowGap, columnGap用于设置子组件之间的缝隙。子组件和父组件边缘之间的缝隙并不受影响。

注意这是ReactNative 0.71才引入的新Style属性。之前的版本可以采用这种方式模拟gap

参考

  1. ReactNative Flexbox
  2. Yoga
commented

适配

指定元素宽高

ReactNative有3种方式指定宽高尺寸:

  1. 绝对尺寸
  2. Flex方式
  3. 百分比方式

绝对尺寸

All dimensions in React Native are unitless, and represent density-independent pixels.

  1. 无单位
  2. 密度无关的像素,即逻辑像素(设备独立像素)

逻辑像素(dp)

简单回顾下逻辑像素概念:同样的尺寸在不同的设备(低分辨率和高分辨率)上看起来是“一样的”。

无单位

iOSAndroid平台里逻辑像素单位是不同的,ReactNative是作为夸平台技术直接采用无单位方式表示逻辑像素。

App适配

参考

  1. 像素密度
  2. Size Matters: How I used React Native to make my App look great on every device
  3. 聊聊React Native屏幕适配那些事儿
  4. Different UI/UX on Android and iOS when develop React Native app
  5. To Make Apps Accessible, Make Them Compatible with Different Devices
commented

定义样式

  1. RN中利用JS声明样式;
  2. RN核心组件都有个style属性,用于定义组件样式;并且可以传个对象数组用于实现样式层叠。
  3. 使用StyleSheet模块将组件的样式统一放在一个地方,可简化组件render函数,提高组件的可读性。

StyleSheet API

StyleSheet API模块除了包含常用的Util函数(比如StyleSheet.create)外,还有一些常用属性:

  1. hairlineWidth
  2. absoluteFill
  3. absoluteFillObject

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的代码注释里可以看出主要基于两个目的:

  1. 代码质量
  • 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
  1. 性能
  • 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).

但是从目前实现看只有代码质量这个动机了:

  1. StyleSheet.create会校验实参是否是合法的Style属性(但是从v0.66开始不会再校验了,而是利用TS声明方式提示);
  2. 借助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)只是原封不动的返回实参。

总结

综上所述:

  1. 从render函数中移除具体的样式内容,可以使代码更清晰易懂;
  2. 样式命名也可以对render函数中的组件增加语义化的描述
// 不推荐 ❌
<View style={{ backgroundColor: '#fff‘ }} />

// 推荐 ✅
const styles = StyleSheet.create({
  wrapper: {
    backgroundColor: '#fff‘ 
  }
});

// 说明View作为wrapper
<View style={styles.wrapper} />
  1. style对象作为只读对象对待
    开发阶段StyleSheet.create函数会Object.freezestyle对象的属性,不要直接修改StyleSheet.create返回值的属性。

参考

  1. StyleSheet API
  2. What is the point of StyleSheet.create
  3. RN的stylesheet简介