一位React工程师体验使用React Native

首先

我最初使用React进行web开发,但现在开始涉足使用React Native进行移动端开发。作为一个之前使用React的人,我想分享一下学习React Native的感受。本文将参考React Native官方文档[1]来进行书写。

前提 tí) – precondition/assumption

可以使用React
可以使用TypeScript
是第一次使用移动端

如果你想了解环境搭建和简单的操作确认,请参考上一篇文章[1]。

React Native的重要性

现在移动设备上的操作系统主要分为Android和iOS,而且它们各自所使用的编程语言也不同。过去,iOS主要使用Objective-C,而Android主要使用Java。然而,最近,iOS开始使用Swift,而Android则使用Kotlin,这意味着可能存在最多四种不同的代码。显然,从开发效率的角度来看,这是不理想的。为了解决这个问题,一些跨平台开发框架如React Native已经被开发出来。

必须使用React Native吗?

近年有一個常被比較的框架是Flutter。一般常有一種討論叫做「Flutter vs React Native」,但從開發成本的角度來看,相比Flutter,React Native明顯更有效率。在Flutter中需要學習一個新的語言Dart,學習成本較高,而React Native則可以利用React的知識進行開發,學習成本相對較低。從性能角度來看,Flutter可能更好,但如果對性能要求不高的話,使用React Native更方便快捷。但需要注意的是,雖然React Native可以直接使用,但還是需要學習一些基本知識。

我想从这里开始谈谈React Native的概念。

React Native的组件

在React Native官方文档中,将组件分为几种类型来调用。

本地组件-负责在每个操作系统中支持视图的部分备份组件。

核心组件
React Native提供了一个可立即使用的本地组件集合。

社区贡献的组件
由React Native社区开发的各个操作系统独特的本地组件。

React Native组件是用来与不同操作系统特定部分进行交互的,而核心组件是将这些组件整合在一起的。大概我们通常会使用核心组件。如果需要的核心组件不够,可能需要寻找一些由社区贡献的组件来满足需求。

我会放一张公式类的维恩图像。

react_native_component.png

理解這個圖的前提是已經理解React Components,因為React Native Components只是React Components的一部分。

对于React Native的语法,请提供一种中文本地化的翻译选项:

现在开始正题。我想要来看一下React Native独有的部分,这是React中没有的特点。

理解App.tsx文件

让我们首先解读通过expo init命令创建的App.tsx模板。我已将文本更改为“Hello,World”。

import { StatusBar } from "expo-status-bar";
import React from "react";
import { StyleSheet, Text, View } from "react-native";

export default function App() {
  return (
    <View style={styles.container}>
      <Text>Hello,World</Text>
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
});

首先,似乎正在导入React,但这与React不同,是否是必要的?也许是在内部使用它吧。当我调查StatusBar时,发现它是指显示在移动设备顶部的电池电量和信号状况图标的区域,称为状态栏。View和Text是React Native的核心组件,稍后会进行详细说明。看起来对View应用了样式,设置了一个名为container的styles对象属性,并编写了样式。关于样式,稍后会有详细说明,但在这里需要使用驼峰命名法,与React中的内联样式相同。

我大概看了一下,基本框架和React没有太大差别,所以只需记住各个组件就可以了。

组件的属性

查看React Native的参考资料,可以看到props在iOS和Android操作系统中有许多差异。此外,props的数量本身相当多,要全部记住非常困难。在本文中,主要不涉及特定于每个操作系统的props,并且只介绍可能会使用到的props。如果您想了解更多,请参考官方参考资料[3]。

观看

以下是React Native中最基本的组件,你可以将其视为类似于div标签(严格来说有些不同)。

iOS的SafeAreaView

在iOS设备上,由于物理差异,如刘海和圆角等,安全渲染的范围会有所不同。使用该组件可以在安全范围内呈现内容。

文本

如果要写文字,则基本上使用这个。即使直接写在视图上也不会被反映出来。

在Android和iOS中,可以为字符串范围添加特定的格式,为了实现这个功能,可以对文本进行嵌套。通过对嵌套文本进行样式设置,可以为任意范围添加格式。

・布局非常特殊
此外,文本在布局方面也非常特殊。测试中的元素可能不是长方形,会根据行的结束进行换行。

・样式的继承受到限制
文本样式只能由Text继承。换句话说,在View中对文本进行样式化是无效的。官方建议如果想要重用特定样式的Text,则应定义一个基于现有Text进行扩展的组件。在Text内部,样式是继承的,所以在嵌套的Text中,父级样式将被应用。

图像

使用图片时使用。通过将参考传递给道具的资源,可以显示图像。以下是官方的示例。


import React from 'react';
import { View, Image, StyleSheet } from 'react-native';

const styles = StyleSheet.create({
  container: {
    paddingTop: 50,
  },
  stretch: {
    width: 50,
    height: 200,
    resizeMode: 'stretch',
  },
});

const DisplayAnImageWithStyle = () => {
  return (
    <View style={styles.container}>
      <Image
        style={styles.stretch}
        source={require('@expo/snack-static/react-native-logo.png')}
      />
    </View>
  );
}

export default DisplayAnImageWithStyle;

从[3]中引用。

输入

只需一個選項,請將以下原文以中文進行翻譯:入力で使用します。全部說明將會變得很冗長,所以我們在下面舉例說明。

import React from "react";
import { SafeAreaView, StyleSheet, TextInput } from "react-native";

const UselessTextInput = () => {
  const [text, onChangeText] = React.useState("Useless Text");
  const [number, onChangeNumber] = React.useState(null);

  return (
    <SafeAreaView>
      <TextInput
        style={styles.input}
        onChangeText={onChangeText}
        value={text}
      />
      <TextInput
        style={styles.input}
        onChangeText={onChangeNumber}
        value={number}
        placeholder="useless placeholder"
        keyboardType="numeric"
      />
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  input: {
    height: 40,
    margin: 12,
    borderWidth: 1,
    padding: 10,
  },
});

export default UselessTextInput;

从[3]中摘录

在使用中有一个有趣的方法是将state的setter放入onChangeText中,这样可以在输入过程中同时反映并保持在state中。我想让人担心的是,在使用TypeScript时,如何区分number和string。如果有时间的话,我会进行验证并更新。

・底部有一条边框
TextInput默认在视图的底部具有边框。这是由系统提供的背景图像设置的填充,并且无法更改。为了避免这个问题,需要明确地不设置高度,这样系统才能在适当的位置显示边框,或者将underlineColorAndroid设置为透明以不显示边框。请查看官方参考资料以了解更多详细信息。

滚动视图

我直接翻译了参考资料,但有一些地方我不太理解,所以可能有些难以理解。

如果想要滚动,可以使用它。在React Native中,如果要使用View进行显示,即使尝试渲染大于显示屏幕区域的内容,也不会自动滚动。因为很少有不需要滚动的情况,所以基本上会使用它吧。

「所有的父视图都需要进行高度限制。在参考文献中,使用bounded这个词来描述,但对于该翻译是否正确存有疑问。限制高度的方法有两种,一种是直接设置视图的高度(不推荐),另一种是给所有父视图设置高度。参考文献中指出如果忘记使用{flex:1}会导致错误,但我并不太明白。以下是一个示例:”

import React from 'react';
import { StyleSheet, Text, SafeAreaView, ScrollView, StatusBar } from 'react-native';

const App = () => {
  return (
    <SafeAreaView style={styles.container}>
      <ScrollView style={styles.scrollView}>
        <Text style={styles.text}>
          Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
          eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
          minim veniam, quis nostrud exercitation ullamco laboris nisi ut
          aliquip ex ea commodo consequat. Duis aute irure dolor in
          reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
          pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
          culpa qui officia deserunt mollit anim id est laborum.
        </Text>
      </ScrollView>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: StatusBar.currentHeight,
  },
  scrollView: {
    backgroundColor: 'pink',
    marginHorizontal: 20,
  },
  text: {
    fontSize: 42,
  },
});

export default App

引自[3]。

可以通过将此props设为false来隐藏滚动条。

将此道具设为true即可支持水平滚动。

与FlatList的不同之处
由于ScrollView一次性渲染所有子组件,因此存在性能问题。FlatList延迟渲染项目,仅在项目即将显示时进行渲染,并通过删除大量滚动到屏幕外的项目来节省内存和处理时间。FlatList还支持项目间距、多列和无限滚动加载等功能,非常方便。

写到这里我突然想到,也许ScrollView这个组件用得并不多吧?根据不同情况需要用来分别对待吗?

样式表

我們到目前為止都在使用這個,主要是為了使用CSS樣式。雖然沒有什麼特別要說的,但我覺得React Native和Tailwind CSS的相容性可能不太好。根據我的調查,使用一些庫好像可以解決這個問題,但考慮到VSCode的智能感應等問題,不知道可行性如何。

我认为CSS in JS在React Native中特别适用,因为它可以几乎和Web开发一样进行描述,有助于降低开发成本。我使用的是styled-components写法,所以不太会遇到困难。

按钮

这是一个对应于按钮的组件。我认为看一下会更快明白,所以下面附上一个例子。

<Button
        title="Press me"
        disabled
        onPress={() => Alert.alert('Cannot press this one')}
      />

[3]引述自。

按照上述文本的要求,以下是一种中文的表达方式:

似乎必须使用onPress和title作为props。通过title可以确定按钮的显示,但是无法使用children进行设置吗?似乎可以使用Alert.alert来实现类似于window.alert的功能。在这个例子中,设置为disabled会导致按钮看起来无法使用。如果想要动态确定,可以将一个布尔变量放在disabled的位置。

转换

这个Switch不是指switch语句,而是指用于切换的按钮(可能有名称)。它是常见于参考资料中的切换暗模式部分的一种。以下是示例。

      <Switch
        trackColor={{ false: "#767577", true: "#81b0ff" }}
        thumbColor={isEnabled ? "#f5dd4b" : "#f4f3f4"}
        ios_backgroundColor="#3e3e3e"
        onValueChange={toggleSwitch}
        value={isEnabled}
      />

通过点击,boolean型变量将切换,并根据value的值进行多种变化。

可触摸性

当受到押压时,被包裹的视图的不透明度会减小,变得昏暗。

<TouchableOpacity
        style={styles.button}
        onPress={onPress}
      >
        <Text>Press Here</Text>
      </TouchableOpacity>

[3]的引用如下:

平面列表

如前所述,用于进行列表显示。

要表示的数组数据。

渲染项目
根据数据来决定显示什么的函数。

附加数据。下面的示例中,为了重新渲染,传递了state。

keyExtractor 指定用于替代默认 key 属性的 id。

如果内容超出了渲染区域,内部状态将不会被保留。
根据FlatList的规定,超出渲染区域的内容的内部状态不会被保存。因此在更新状态和渲染时需要注意。

当内容以异步方式在屏幕外渲染时,可以更快地滚动,但可能暂时显示空白内容。

import React, { useState } from "react";
import { FlatList, SafeAreaView, StatusBar, StyleSheet, Text, TouchableOpacity } from "react-native";

const DATA = [
  {
    id: "bd7acbea-c1b1-46c2-aed5-3ad53abb28ba",
    title: "First Item",
  },
  {
    id: "3ac68afc-c605-48d3-a4f8-fbd91aa97f63",
    title: "Second Item",
  },
  {
    id: "58694a0f-3da1-471f-bd96-145571e29d72",
    title: "Third Item",
  },
];

const Item = ({ item, onPress, backgroundColor, textColor }) => (
  <TouchableOpacity onPress={onPress} style={[styles.item, backgroundColor]}>
    <Text style={[styles.title, textColor]}>{item.title}</Text>
  </TouchableOpacity>
);

const App = () => {
  const [selectedId, setSelectedId] = useState(null);

  const renderItem = ({ item }) => {
    const backgroundColor = item.id === selectedId ? "#6e3b6e" : "#f9c2ff";
    const color = item.id === selectedId ? 'white' : 'black';

    return (
      <Item
        item={item}
        onPress={() => setSelectedId(item.id)}
        backgroundColor={{ backgroundColor }}
        textColor={{ color }}
      />
    );
  };

  return (
    <SafeAreaView style={styles.container}>
      <FlatList
        data={DATA}
        renderItem={renderItem}
        keyExtractor={(item) => item.id}
        extraData={selectedId}
      />
    </SafeAreaView>
  );
};

・・・

我想,这可能是为了替代map函数进行渲染。

节目列表

这是一个类似于带有标题的FlatList的东西。由于几乎相同,所以请参考下方的示例程序来使用。

import React from "react";
import { StyleSheet, Text, View, SafeAreaView, SectionList, StatusBar } from "react-native";

const DATA = [
  {
    title: "Main dishes",
    data: ["Pizza", "Burger", "Risotto"]
  },
  {
    title: "Sides",
    data: ["French Fries", "Onion Rings", "Fried Shrimps"]
  },
  {
    title: "Drinks",
    data: ["Water", "Coke", "Beer"]
  },
  {
    title: "Desserts",
    data: ["Cheese Cake", "Ice Cream"]
  }
];

const Item = ({ title }) => (
  <View style={styles.item}>
    <Text style={styles.title}>{title}</Text>
  </View>
);

const App = () => (
  <SafeAreaView style={styles.container}>
    <SectionList
      sections={DATA}
      keyExtractor={(item, index) => item + index}
      renderItem={({ item }) => <Item title={item} />}
      renderSectionHeader={({ section: { title } }) => (
        <Text style={styles.header}>{title}</Text>
      )}
    />
  </SafeAreaView>
);
・・・

从〔3〕中引用。

最后

我简单地浏览了概念和语法,如果理解了就可以轻松地使用。只是,我发现需要记住的东西比我想象中多,所以我希望在实际开发之后能测试一下使用感受。

文献参考。

【1】:引言
【2】:在Ubuntu20.04LTS上尝试React Native和Expo
【3】:核心组件和API

广告
将在 10 秒后关闭
bannerAds