概述
本文主要介绍基于 React Native 的混合开发,掌握:使用Flexbox 实现界面布局、界面布局和长列表呈现、接入第三方 Native 组件,以及 React Native 架构的实现原理。
- 简介
- 搭建开发环境
- 基础语法
- 架构原理
简介
- 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 Native Facebook JavaScript(React) JSCore Android、IOS 一般 整体 APP 难 Weex Alibaba JavaScript(Vue) V8 Android、IOS、Web 较快 单页面 易 Flutter Google Dart Flutter engine Android、IOS、Fuchsia 较快 整体 APP 一般 - H5开发 Web APP HTML、CSS、JavaScript
移动 APP 的开发模式对比:
| 开发模式 | 运行环境 | 编程语言 | 可移植性 | 开发速度 | 性能 | 学习成本 |
|---|---|---|---|---|---|---|
| 原生开发 | Android、IOS | Java、Objective-C | 差 | 慢 | 快 | 高 |
| 混合开发 | Android、IOS | JavaScript、Dart(Flutter) | 一般 | 一般 | 较慢 | 一般 |
| Web 开发 | 浏览器、WebView(移动端的内置浏览器) | HTML、CSS、JavaScript | 好 | 快 | 慢 | 低 |
React Native 的优势
- 开发体验好:
- 用统一的代码规范开发移动端程序,不用关注移动端的差异
- 开发成本低
- 开发一次,可以生成 Android 和 IOS 两个系统上的App
- Learn once, write anywhere
- 学习成本低
- 只要掌握 JavaScript 和 React,就可以进行移动端开发了。
React Native 的不足
- 不成熟
- 在官网看到 React Native 最新的版本是0.64,还处于开发阶段,还没有发布正式的1.0版本。
- 项目版本更新维护较频繁,学习成本高
- 试错成本高,有些问题较少解决方案,易耽误开发进度
- 性能差
- 整体性能仍不如原生
- 兼容性差
- 涉及底层的功能,需要针对 Android 和 IOS 双端单独开发
搭建开发环境
基础语法
- 掌握 React
- StyleSheet:用来声明样式的API
- Flexbox:弹性布局
- 组件和API
- 路由与导航
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>
3. Flexbox
Flexbox - 术语
- 容器(container)
- 采用 Flex 布局的元素,成为 Flex 容器 (flex container),简称”容器“
- 项目(item)
- 容器所有子元素,成为 Flex 项目(flex item),简称项目
- 主轴(main axis)
- 交叉轴
Flexbox - Web:
Flexbox - RN:
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 的组件
RN 组件最后也会转成原生组件
组件对比:
核心组件
【React Native 组件,从 React Native 引入】
- 基础组件
- 交互组件
- 列表视图
- IOS 独有组件
- Android 独有组件
- 其他
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() 时间渐变效果【用得最多】
第三方组件
- 需要单独安装的组件
- 使用步骤
- 安装
- 配置
- 使用
WebView
- 安装
- yarn add react-native-webview
- 配置
- 使用
- 直接指定 uri 地址
- 直接渲染 html 代码
Picker
- 安装
- yarn add @react-native-picker/picker
- 配置
- github.com/react-nativ…
- 注意:不同版本的配置方式不同
- 使用
- 注意平台之间的差异(Android | IOS)
Swiper(轮播效果)
- 安装
- yarn add react-native-swiper
- 使用
- 注意Swiper要放在 ScrollView 里面
AsyncStorage(相当于pc的localstorage)
- 安装
- yarn add @react-native-async-storage/async-storage
- 配置
- 使用
- 增删改查
Geolocation
- 安装
- yarn add @react-native-community/geolocation
- 配置
- github.com/react-nativ…
- 添加获取定位信息的授权许可
- 使用
- 通过手机,获取经纬度信息
Camera(摄像头)
- 安装
- yarn add react-native-camera
- 配置
- github.com/react-nativ…
- 添加使用摄像头的授权许可
- 使用
- 拍照、扫码、人脸识别...
ImagePicker
- 安装
- yarn add react-native-image-picker
- 配置
- github.com/react-nativ…
- 与 Camera 一致
- 使用
- 调用摄像头、访问相册...
自定义组件
- 工程师自己写的组件
- react 是面向组件开发的(所有内容都是组件)
- 这里的自定义组件是指:具有特定功能,可以重复使用的公共组件
5. 路由与导航
- 简介
- RN 中的路由是通过 React-Navigation 来完成的
- React 中通过react-router实现路由
- RN 0.44之前,React-Navigation 在核心中维护,0.44之后,独立维护。
- 本节使用React-Navigation 5.x版本
- 基础组件
- 安装:
- 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>
)
}
- Stack 导航
- 简介
- RN 中默认没有类似浏览器的history对象
- 在 RN 中跳转之前,先将路由声明在stack中
- 安装
- yarn add @react-navigation/stack
- 使用
- import { createStackNavigator } from '@react-navigation/stack'
- const Stack = createStackNavigatior();
- BottomTab 导航
- Drawer 导航
- MaterialTopTab 导航
6. 架构原理
现有架构
稍微扩展一下
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解析成原生的布局,这里的解析可以称为渲染,操作过程如下:
还可以这样理解
了解了渲染过程后会更加理解下面这张图
从启动过程,来理解架构设计
从应用过程,来理解架构设计
来看一些代码
新架构
RN 重构的背景
- 之前的版本,存在诸多性能问题
- 受到 Flutter 等后起之秀的压力
- 2018年6月,提出重构计划
新旧架构对比
整体来说,做了三大改动
- 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的序列化和反序列化。
优化 Bridge层
- Fabric
- Fabric 是整个架构中的新 UI 层
- 简化了之前的渲染流程
- Turbo Modules
- 通过JSI,可以让JS 直接调用Native模块,实现同步操作
- 实现 Native 模块按需加载,减少启动时间,提供性能
精简核心模块
- 将 react-native 核心包进行瘦身
- RN 推出多年,其核心包太过臃肿
- 有些包在项目中用不到,每次也要引入,造成资源浪费
- 非必要的包,移到社区模块,单独维护
- 例如 AsyncStorage、Webview等
原计划2020年第四季度重构完成,现在看来时间会延后,部分优化已经完成 ~