0%

React-Native

Why React Native

React Native 和前端技术生态重合度很高,学习成本低, 一份源码可以同时编译成 Android 和 iOS 原生应用. 适合更新迭代较快的项目
同时 其底层架构师根植于移动端原生API 可以自定义原生模块 以及优化性能

本质(nature)

在 Android 开发中是使用 Kotlin 或 Java 来编写视图;在 iOS 开发中是使用 Swift 或 Objective-C 来编写视图。在 React Native 中,则使用 React 组件通过 JavaScript 来调用这些视图。在运行时,React Native 为这些组件创建相应的 Android 和 iOS 视图。由于 React Native 组件就是对原生视图的封装,因此使用 React Native 编写的应用外观、感觉和性能与其他任何原生应用一样。我们将这些平台支持的组件称为原生组件。

在较早的React Native中通过称为Bridge的异步机制完成js与原生代码之间的通信,0.74版本开始默认使用Bridgeless mode,调用JSI访问原生代码从而提高性能和响应速度

React Native 允许您为 Android 和 iOS 构建自己的 Native Components(原生组件),以满足您开发应用程序的独特需求。React Native 还包括一组基本的,随时可用的原生组件,您可以使用它们来构建您的应用程序。这些是 React Native 的核心组件

环境配置

React Native项目

1
2
3
npx @react-native-community/cli@latest init AwesomeProject
yarn start # 启动 Metro 构建工具
yarn android

Android设备

  • USB连接Android手机
  • AVD(Android Virtual Device)模拟器

Android Studio—>工具栏Device Manager—>Add a new device—>Create Virtual Device—>选择任意设备—>Next—>UpsideDownCake API Level 34 image

开发

Android Studio开发js/ts很不习惯,可以使用vscode调试,添加react-native-tool,配置emulator到环境变量以使能被vscode调起

1
2
3
4
npm i -g react-native
react-native init mobileApp
npm run android
npm run ios

项目终端执行 npm run android 输出
1
2
3
4
5
6
7
$ react-native run-android
info Launching emulator
...
info Installing the app
> IDLE
> IDLE
...

安装完毕后
会另外启动四个窗口

  • NodeJS 窗口
  • emulator\crashpad_handle.exe
  • emulator\qemu\windows-x86_64\qemu-system-x86_64.exe
  • Android Emulator 移动设备界面

调试
在Android Emulator界面上按 Ctrl + M 弹出Emulator菜单,在菜单中点击Open DevTools启动React Native Cli自带DevTools

打包

1
2
cd android
./gradlew assembleRelease

android emulator

命令行输入emulator —help查看工具参数手册

1
2
emulator -list-avds
...

对于应用crash导致系统崩溃,尝试不加载snapshot后冷启动
1
2
3
4
emulator -avd myavd -no-snapshot-load -no-snapshot-save
emulator -avd myavd -cold-boot
# 擦除用户数据(恢复设置)
emulator -avd myavd -no-snapshot-load -wipe-data

将本地文件放入虚拟设备
1
avd push D:\Download\Game.apk /sdcard/Download/Game.apk

Internet

Platform

1
2
3
4
5
import {Platform, StyleSheet} from 'react-native';

const styles = StyleSheet.create({
height: Platform.OS === 'ios' ? 200 : 100,
});

Platform.OS返回’ios’ 或者’android’
Platform.version返回OS版本号
Platform.select根据平台返回以平台字符串为key的map中对应的value

1
2
3
4
5
6
const Component = Platform.select({
ios: () => require('ComponentIOS'),
android: () => require('ComponentAndroid'),
})();

<Component />;

Metro

The JavaScript bundler for React Native 打包器

视图

视图是React Native UI的基本组成。视图的概念可以小到屏幕上的一个小矩形元素、可用于显示文本、图像或响应用户输入。甚至应用程序最小的视觉元素(例如一行文本或一个按钮)也都是各种视图。View可以嵌套View。全部都是视图。

核心组件

  • View
  • Text
  • Image
  • ScrollView
  • TextInput
  • StyleSheet
  • Button
  • Switch
  • FlatList
  • SectionList

样式

与网页的不同

  • 子元素不继承样式
  • 驼峰命名fontSize
  • 不使用单位(px等)

StyleSheet:

1
2
3
4
5
6
7
8
9
10
11
12
13
import { StyleSheet, Text } from 'reat-native'

const styles = StyleSheet.create({
sectionTitle: {
fontSize: 24
}
})

return (
<>
<Text style={{styles.sectionTitle}}>
</>
)

css px是逻辑像素,物理像素/逻辑像素叫设备像素比 这里css省略px,强调逻辑像素,具体显示的物理像素数量由设备根据屏幕种类换算

PPI(pixels per inch)每英寸物理像素点

RN中的Flexbox规则和Web中Css FlexBox基本一致,主要的不同就是flex-direction默认不同,css中默认是水平方向,rn中默认是垂直方向。

阴影样式

1
2
3
4
5
6
7
8
shadowBorder:{
shadowColor:'#000',
shadowOffset:{width:0, height:0},
shadowOpacity:0.6,
shadowRadius:8,

elevation:1
}

对应css
1
box-shadow: '0 0 8px 8px #000a'

即iOS 通过 shadowOffset shadowOpacity shadowRadius 来实现阴影,而 Android 则通过 elevation 来实现阴影,
ios

React Native Navigation
React Native 中导航变得很复杂,原因在于页面url不会加入window.history

  • Stack Navigator
  • Tab Navigator
  • Drawer Navigator
    1
    2
    3
    4
    yarn add @react-navigation/native
    yarn add react-native-screens react-native-safe-area-context

    yarn add @react-navigation/native-stack --> stack导航器
    编辑位于 android/app/src/main/java// 下的 MainActivity.kt 或 MainActivity.java 文件。
    添加以下代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //...
    import android.os.Bundle;

    class MainActivity: ReactActivity() {
    // ...
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(null)
    }
    // ...
    }
    Example:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    const Stack = createNativeStackNavigator();

    function App(): React.JSX.Element {

    return (
    // <SafeAreaView style={backgroundStyle}>
    <NavigationContainer>
    <Stack.Navigator initialRouteName='Home'>
    <Stack.Screen name="Home" component={Home} />
    <Stack.Screen name="List" component={PatientList} />
    <Stack.Screen name="Detail" component={PatientDetail} />
    </Stack.Navigator>
    </NavigationContainer>

    // </SafeAreaView>
    );
    }

    响应式

    Dimensions API
    1
    2
    3
    4
    import { Dimensions } from 'react-native'

    const { width, height } = Dimensions.get('screen')//包括状态栏和底部导航
    const { width, height } = Dimensions.get('window')//Android不包括, IOS上与screen相同
    监听屏幕尺寸改变(横竖屏切换,折叠屏)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    useEffect(() => {
    const subscription = Dimensions.addEventListener(
    'change',
    ({ window }) => {
    setWindowSize({
    width: window.width,
    height: window.height,
    })
    })
    return () => subscription?.remove() //组件unmount时移除监听
    }, [])

AndroidManifest.xml

声明安卓应用版本信息,元数据,权限声明等,相应的ios应用是Info.plist

1
<uses-permission android:name="android.permission.CAMERA" />

build.gradle

配置应用的构建过程,包含依赖的仓库和插件等等
使用react-native-vector-icons需要配置build.gradle

1
apply from: file("../../node_modules/react-native-vector-icons/fonts.gradle")

使用icons:
1
2
3
import Icon from "react-native-vector-icons/FontAwesome"

<Icon name="camera" color="#ccc" size={36} onPress={openCamera} />