React Native

2,884 阅读9分钟

概述

本文主要介绍基于 React Native 的混合开发,掌握:使用Flexbox 实现界面布局、界面布局和长列表呈现、接入第三方 Native 组件,以及 React Native 架构的实现原理。

  1. 简介
  2. 搭建开发环境
  3. 基础语法
  4. 架构原理

简介

  • React Native 是 Facebook 在 React.js Conf 2015 上推出了开源框架
    • React Native(简称 RN)是 React 的一个原生(Native)扩展。
    • 它允许我们通过 React 语法,来开发 IOS 和Android 原生应用
  • 资源网站

React Native 是用来开发移动应用的,来看一下移动APP的几种开发模式:

  • 原生开发 原生 APP Android | IOS | Windows:一开始这三大应用平台是并驾齐驱的,后来 Windows 逐渐掉队,现在看到的应用主要是 Android 和 IOS。
  • 混合开发 混合APP React Native | Weex | Flutter
    • 跨平台框架的比较:
    框架所属公司编程语言引擎支持系统性能使用场景学习成本
    React NativeFacebookJavaScript(React)JSCoreAndroid、IOS一般整体 APP
    WeexAlibabaJavaScript(Vue)V8Android、IOS、Web较快单页面
    FlutterGoogleDartFlutter engineAndroid、IOS、Fuchsia较快整体 APP一般
  • H5开发 Web APP HTML、CSS、JavaScript

移动 APP 的开发模式对比:

开发模式运行环境编程语言可移植性开发速度性能学习成本
原生开发Android、IOSJava、Objective-C
混合开发Android、IOSJavaScript、Dart(Flutter)一般一般较慢一般
Web 开发浏览器、WebView(移动端的内置浏览器)HTML、CSS、JavaScript

React Native 的优势

  • 开发体验好:
    • 用统一的代码规范开发移动端程序,不用关注移动端的差异
  • 开发成本低
    • 开发一次,可以生成 Android 和 IOS 两个系统上的App
    • Learn once, write anywhere
  • 学习成本低
    • 只要掌握 JavaScript 和 React,就可以进行移动端开发了。

image.png

React Native 的不足

  • 不成熟
    • 在官网看到 React Native 最新的版本是0.64,还处于开发阶段,还没有发布正式的1.0版本。
    • 项目版本更新维护较频繁,学习成本高
    • 试错成本高,有些问题较少解决方案,易耽误开发进度
  • 性能差
    • 整体性能仍不如原生
  • 兼容性差
    • 涉及底层的功能,需要针对 Android 和 IOS 双端单独开发

搭建开发环境

搭建 React Native 开发环境

基础语法

  1. 掌握 React
  2. StyleSheet:用来声明样式的API
  3. Flexbox:弹性布局
  4. 组件和API
  5. 路由与导航

1. 掌握 React

  • JSX 语法
  • 组件(分类,传参,属性,状态)
  • 生命周期
  • Hook API
  • Redux
  • 常用的安装包

2. StyleSheet

RN 中的样式与 CSS 的不同

  • 没有继承性
    • RN 当中的继承只发生在Text组件上
  • 样式名采用小驼峰命名
    • fontSize VS font-size
  • 所有尺寸都是没有单位
    • width: 100
  • 有些特殊的样式名
    • marginHorizontal(水平外边距),marginVertical(垂直外边距)

RN 样式的声明方式

  • 通过 style 属性直接声明
    • 属性值为对象:<组件 style={ {样式} } />
    • 属性值为数组:<组件 tyle={ [ {样式1}, ..., {样式N} ]} />
  • 在 style 属性中调用 StyleSheet 声明的样式
    • 引入:import { StyleSheet, View } from 'react-native'
    • 声明: const styles = StyleSheet.create({ foo: {样式1} }, bar: {样式2})
    • 使用:<View style={ [ styles.foo, styles.bar ]}> 内容 </View>

image.png

3. Flexbox

Flexbox - 术语

  • 容器(container)
    • 采用 Flex 布局的元素,成为 Flex 容器 (flex container),简称”容器“
  • 项目(item)
    • 容器所有子元素,成为 Flex 项目(flex item),简称项目
  • 主轴(main axis)
  • 交叉轴

Flexbox - Web: image.png

Flexbox - RN:

image.png

Flexbox - 属性

  • flexDirection
    • 声明主轴方向:row(Web默认)| column(RN默认)
  • jusitifyContent
    • 声明项目在主轴上的对齐方式
  • alignItems
    • 声明项目在交叉轴上的对齐方式
  • flex
    • 声明项目在主轴上的尺寸比例

响应式布局

  • Flexbox
  • Dimensions
    • import { Dimensions } form 'react-native';
    • const windowWidth = Dimensions.get('window').width;
    • const windowHeight = Dimensions.get('window').height;

4. 组件和API

简介

  • RN 中的核心组件,是对原生组件的封装
    • 原生组件:Android 或 IOS 内的组件
    • 核心组件:RN 中最常用的,来自 React Native 的组件

image.png

RN 组件最后也会转成原生组件

image.png

组件对比:

image.png

核心组件

【React Native 组件,从 React Native 引入】

  • 基础组件
  • 交互组件
  • 列表视图
  • IOS 独有组件
  • Android 独有组件
  • 其他

image.png

image.png

Image 组件
  • 作用
    • 加载图片
  • 加载方式
    • 本地路径
    • 图片的 UPL 地址
    • 图片的 Base64 字符串
Animated

组件必须经过特殊处理才能用于动画,特殊处理是指把动画的值绑定到属性上,并且在一帧一帧执行动画时避免重新渲染和重新调和的开销,此外,还需要在组件卸载的时候做一些清理工作,使得组件是安全的。

RN 中可以直接使用的动画组件

  • Animated.View
  • Animated.Text
  • Animated.ScrollView
  • Animated.Image

如何创建动画【步骤】

  • 创建初始值
    • Animated.Value() 单个值【例如更改图片的透明度】
    • Animated.ValueXY() 向量值【例如更改元素的位置】
  • 将初始值绑定在动画的组件上
    • 一般将其绑定到某个样式属性下,例如:opacity、translate【X, Y】
  • 通过动画类型API,一帧一帧改变初始值
    • Animated.decay() 加速效果【慢-快-慢】
    • Animated.spring() 弹跳效果
    • Animated.timing() 时间渐变效果【用得最多】

第三方组件

  • 需要单独安装的组件
  • 使用步骤
    • 安装
    • 配置
    • 使用

image.png

WebView
  • 安装
    • yarn add react-native-webview
  • 配置
  • 使用
    • 直接指定 uri 地址
    • 直接渲染 html 代码
Picker
  • 安装
    • yarn add @react-native-picker/picker
  • 配置
  • 使用
    • 注意平台之间的差异(Android | IOS)
Swiper(轮播效果)
  • 安装
    • yarn add react-native-swiper
  • 使用
    • 注意Swiper要放在 ScrollView 里面
AsyncStorage(相当于pc的localstorage)
Geolocation
  • 安装
    • yarn add @react-native-community/geolocation
  • 配置
  • 使用
    • 通过手机,获取经纬度信息
Camera(摄像头)
  • 安装
    • yarn add react-native-camera
  • 配置
  • 使用
    • 拍照、扫码、人脸识别...
ImagePicker
  • 安装
    • yarn add react-native-image-picker
  • 配置
  • 使用
    • 调用摄像头、访问相册...

自定义组件

  • 工程师自己写的组件
    • react 是面向组件开发的(所有内容都是组件)
    • 这里的自定义组件是指:具有特定功能,可以重复使用的公共组件

5. 路由与导航

  1. 简介
  • RN 中的路由是通过 React-Navigation 来完成的
    • React 中通过react-router实现路由
    • RN 0.44之前,React-Navigation 在核心中维护,0.44之后,独立维护。
  • 本节使用React-Navigation 5.x版本
  1. 基础组件
  • 安装:
    • yarn add @react-navigation/native
    • yarn add react-native-reanimated react-native-gesture-handle react-native-screens react-native-safe-area-context @react-native-community/masked-view
  • 链接
    • RN 0.60 后安卓环境自动链接路由(Android 无需任何操作)
    • IOS 下需要手动链接路由(npx pod-install ios)
  • 添加头部组件
    • 将如下代码,放到应用的头部(例如:放到index.js或 App.js文件的头部)
    • import 'react-native-gesture-handle'
  • 添加导航容器
    • 我们需要在入口文件中,把整个应用,包裹在导航容器(NavigationContainer)中(例如:在index.js或App.js文件中)
import 'react-native-gesture-handle';
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native;

export default function APP () {
  return (
    <NavigationContainer>
      {/* 具体应用代码 */}
    </NavigationContainer>
  )
}
  1. Stack 导航
  • 简介
    • RN 中默认没有类似浏览器的history对象
    • 在 RN 中跳转之前,先将路由声明在stack中
  • 安装
    • yarn add @react-navigation/stack
  • 使用
    • import { createStackNavigator } from '@react-navigation/stack'
    • const Stack = createStackNavigatior();
  1. BottomTab 导航
  2. Drawer 导航
  3. MaterialTopTab 导航

6. 架构原理

现有架构

image.png

稍微扩展一下

image.png

React 运行之前会进行打包,打包之后得到JS代码,经过 RN 中 JS解析引擎 JS Core解析后消息传递给 Bridge。Shadow Tree定义UI渲染以及相关的交互功能,Native Modules 是原生模块例如蓝牙摄像头网络等。通过JSON【公共的数据交互语言】进行交互,例如JS代码经过Bridge处理转换成JSON之后就可以传递给Native。

为了了解架构设计需要了解架构下的线程模型

  • JS 线程
    • JS 代码的执行线程,将源码通过 Metro 打包后,传给 JS 引擎进行解析
  • Main 线程(UI线程或原生线程)
    • 主要负责原生渲染(Native UI)和调用原生模块(Native Modules)
  • Shadow 线程(Layout 线程)
    • 创建 Shadow Tree 来模拟 React 结构树(类似虚拟DOM)
    • 再由 Yoga 引擎将 Flexbox 等样式,解析成原生平台的布局方式

再来解释一下 Shadow 线程,首先得了解一个前提,Android 或 ios 中的布局是不支持 Flexbox的,所以需要把我们在RN中写的Flexbox解析成原生的布局,这里的解析可以称为渲染,操作过程如下:

image.png

还可以这样理解

image.png

了解了渲染过程后会更加理解下面这张图

image.png

从启动过程,来理解架构设计

image.png

从应用过程,来理解架构设计

image.png

来看一些代码

image.png

新架构

RN 重构的背景
  • 之前的版本,存在诸多性能问题
  • 受到 Flutter 等后起之秀的压力
  • 2018年6月,提出重构计划
新旧架构对比

image.png

整体来说,做了三大改动

  • JavaScript 层:
    • 支持 React16+ 的新特性(0.59之后就支持了)
    • 增强JS静态类型检查(CodeGen)
    • 引入JSI,允许替换不同的JavaScript引擎
  • Bridge 层:
    • 划分成 Fabric 和 TurboModules 两部分,分别负责UI管理和 Native模块
  • Native 层:
    • 精简核心模块,将非核心部分拆分出去,做且社区模块,独立更新维护

CodeGen

  • CodeGen 是 Facebook 推出的代码生成工具
  • 通过 CodeGen,自动将Flow 或 TypeScript等有静态类型的代码翻译成Fabric 和 TurboModules 使用的接口文件
  • 加入类型约束后的作用
    • 减少了数据类型错误
    • 减少了数据验证的次数,提供了通信性能

JSI【JavaScript Interface】

可以使JS运行在不同的JS引擎上,因为JSI是由C++编写的轻量型框架,JS可以直接获得C++对象的引用,从而允许JS与Native直接调用,省去了跨线程的通信调用以及JSON的序列化和反序列化。

image.png

优化 Bridge层

  • Fabric
    • Fabric 是整个架构中的新 UI 层
    • 简化了之前的渲染流程
  • Turbo Modules
    • 通过JSI,可以让JS 直接调用Native模块,实现同步操作
    • 实现 Native 模块按需加载,减少启动时间,提供性能

精简核心模块

  • 将 react-native 核心包进行瘦身
    • RN 推出多年,其核心包太过臃肿
    • 有些包在项目中用不到,每次也要引入,造成资源浪费
  • 非必要的包,移到社区模块,单独维护
    • 例如 AsyncStorage、Webview等

image.png

原计划2020年第四季度重构完成,现在看来时间会延后,部分优化已经完成 ~