react native 环境搭建体验

1,150 阅读16分钟

1 背景

本人是 vue 技术栈,并非 react 技术栈。只是临时有需求,所以周末临时研究了一下。写这篇文章进行分享的同时,也顺便给自己记个笔记。

2 两种脚手架

在官网的环境搭建指导上,我们可以看到有两种脚手架可供使用:

  1. Expo CLI
  2. React Native CLI

Expo CLI 是 Expo 框架的其中一个组件,用它来搭建的工程是基于 Expo 框架那一套生态的。React Native CLI 则是 React Native 原版的脚手架。用不同脚手架搭建出来的工程,由于使用的技术方案有所区别,所以在环境依赖、支持的能力、使用效果上也会有所差异。

2.1 进行一个简单的比较

Expo CLIReact Native CLI
依赖工具(安卓)nodejsnodejs、JDK、Android Studio
依赖工具(ios)nodejsnodejs、Xcode、CocoaPods
开发系统(安卓)windows、mac、linuxwindows、mac、linux
开发系统(ios)windows、mac、linuxmac
调试设备形态(安卓)网页、真机、虚拟机(需要自己安装)真机、虚拟机(Android Studio自带,也可自己另装)
调试设备形态(ios)网页、真机真机、虚拟机(Xcode自带)

2.2 关于调试设备

以安卓为例,我们在移动开发的过程中,IDE与安卓设备的对接是靠 adb 完成的,通过 adb 去做传输apk文件、发送操作命令、获取控制台打印输出等操作。

adb 连接方式主要有两种:usb 线直连、网络连接。在真机上调试的时候两种都可以选用,甚至两种可以同时结合使用,先直连再通过网络转发。而在虚拟机上调试的时候一般只能用第二种,所以我们选用安卓模拟器的时候需要确认它能支持暴露 adb 的网络服务。React Native 的对接方式也是这样。

而 Expo 这套框架,基于 React Native 进行了进一步封装,进行了能力增强,使得它具备了一些特殊的能力。

比如,可以在不依赖 adb 的情况下进行调试。

提供了一款名叫 Expo Go 的 app 作为宿主应用,让用户预先安装在手机上。用户使用 Expo Go 扫码或者输入URL的时候,会通过网络请求把我们的应用作为一个子应用加载过去。类比一下微信小程序就好理解了。当然,Expo Go 的这种玩法一般只用于开发阶段,实际上打包发布应用市场的时候不需要这么玩,可以打包成独立的apk包。

又比如,在网页上也能预览app效果调试。

基于 react-native-web 实现

2.3 总结一下两套方案的优劣

  • Expo CLI
    • 优点:环境的依赖程度低。甚至 Expo 官方提供的 snack 工具还能让我们在浏览器中使用在的IDE和模拟器能开发应用,连本地的 nodejs 都不需要。用这款工具可以让新手无需关心环境问题快速入门。
    • 缺点:默认只支持 React Native APIExpo API 本身提供的功能。有些底层的功能(比如蓝牙、TCP连接等)需要使用一些原生模块或者和原生代码混合编程的话,就仍然需要折腾环境。对新手而言,一波操作下来会比直接用 React Native CLI 更麻烦。
  • React Native CLI
    • 优点:功能强大,可以与原生代码混合编程,使用原生模块也比较方便。
    • 缺点:搭建开发环境比较麻烦。

下面会分别使用这两种方式进行环境搭建

下文会使用缩写 RN 来表示 React Native。

3 使用 Expo CLI

首先,上官方教程链接。 过程还是比较傻瓜式的,如果没什么意外的话,按照官方文档去做就会很顺利。因不同电脑网络环境和 npm 源配置不同,如果下载依赖遇到了问题,更换合适的 npm 源自行解决即可。

如果没有 nodejs 环境,需要下载 nodejs 进行安装。

全局安装 expo-cli:

npm install -g expo-cli

安装完成之后,nodejs 的相关目录下生成一个 expo.cmdexpo.sh 等脚本文件,我们就可以使用 expo 命令来调用 expo-cli 脚手架了。

创建新项目的时候,就用 expo-cli 脚手架来创建:

expo init AwesomeProject

进入目录之后。像一个普通的前端工程一样,用 npm 命令运行运行相关程序启动前端工程。

cd AwesomeProject
npm start

如果使用 windows 的 cmd 去运行这个命令的话,由于字体问题,出来的二维码会变成这个样子:

虽然直接复制那个链接 exp://192.168.31.38:19000 拿来使用也是可以的,但总规避问题也不妥。所以我这里找了个别的办法来正常打印二维码。

用 git bash 操作

winpty npm.cmd start # 不加 winpty 的话,二维码打印不出来

二维码可以正常展示。

现在应用已经构建完了,按照官方文档的指导,我们只需要在手机上,安装宿主应用 Expo Go,用 Expo Go 去扫码就可以加载这个工程编出来的应用了。

点击文档里面给的 Expo Go 下载连接,跳转到的是 Expo 的官网。这时候如果你习惯性地点击导航栏上面的 Tools -> Android Client,就会发现它跳转到了被墙掉的网站上。

网站被墙了,但开发总还是要开发的,于是一通找看看有没有什么别的途径能下载到这个软件:

  1. 在自己手机上的小米应用商店搜了一下,没有。
  2. 找了 Expo Go 所在的 github 链接,企图在上面找到编译好的发布包。可惜这个仓库并不提供发布包。
  3. 在 Expo 官网的这个页面找到了可以直接下载的 apk 包。点进去往下滚两三页就可以看到了。

这是 Expo Go 的主页面。

用它扫描完二维码后,应用可以正常被加载出来。

4 使用 React Native CLI

4.1 下载安装几个工具

准备好下面几个工具,已经装过的就不必再安装了

  1. 安装 下载 nodejs 。直接下载一路 next 安装即可。
  2. 下载 openjdk 18,解压到自己想要放置的目录中后配置环境变量。选择 openjdk 18 是因为这个版本的生命周期正处于 GA 状态,而 RN 官方给的 openjdk 11的链接已经被归类为历史版本了。
  3. 下载 android studio 进行安装。记得勾选一下安装虚拟设备的选项,其他直接一路next。

4.2 新建一台模拟器

使用 android studio 的虚拟设备功能,新建一台模拟器

截图11.png 截图12.png 截图13.png 截图14.png 截图15.png 截图16.png 截图17.png 截图18.png

4.3 配置 android sdk

官方文档上面有个步骤要求点开一些界面,勾选特定版本的 sdk 然后下载。这里因为把步骤顺序换了一下,已经不需要这个操作了,因为创建虚拟设备的时候相应版本的 sdk 会自动下载下来。

剩下的只有一个添加环境变量的操作:添加 ANDROID_HOME 环境变量,并把 %ANDROID_HOME%\platform-tools 添加到 Path 环境变量中。

4.4 创建工程

注意,如果之前有全局安装过 react-native-cli 这个 npm 包,需要先卸载掉。

npm uninstall -g react-native-cli

现在官方推荐的是直接用 npx 运行它,如果电脑上没有这个包,会自动下载最新版本。

创建工程

npx react-native init AwesomeProject

4.5 运行方式1

启动 Metro 服务

npx react-native start

让 Metro 服务保持运行状态,开启另一个命令行窗口,同样切到 RN 工程目录下,执行下面这句命令。

npx react-native run-android

注意,这种运行方式不需要使用 android studio 打开工程,但要保证你的安卓模拟器是属于开机状态的。因为脚本尝试自动启动模拟器可能会遇到一些问题,导致连不上,为稳妥起见还是提前开起来。

截图31.png

4.6 运行方式2

启动 Metro服务

npx react-native start

用 android studio 打开 RN 工程里面的 android 子目录。这个目录实际为一个 android studio 工程,可以被 android studio 识别。

点击 android studio 右上角的运行按钮。

截图32.png

4.7 可能遇到的一些问题

  1. 安卓模拟器崩溃且无法再启动起来。遇到这种情况就删除掉这个虚拟设备重新创建一个,要不了几秒钟时间。
  2. 使用方式 1 运行 app 时,报错说找不到设备或模拟器。可以关闭 android studio、模拟器、命令行窗口或直接重启电脑,重新尝试运行项目。另外需要注意一下,如果是先启动 adb 服务再启动模拟器的话,adb 可能会查找不到模拟器,用下面的容器化方式尤其容易遇到这个问题。
  3. 使用方式 2 运行 app 时,遇到报错“Waiting for Target Device to Come Online”,解决方式同上。
  4. 使用方式 2 运行 app 时,遇到报错“Unable to load script.Make sure you are either running a Metro server or ......”,请参考这条stackoverflow 上面的回答。我这里是在 cmd 执行了 adb reverse tcp:8081 tcp:8081 之后就好了。

5 使用 React Native CLI(容器化)

5.1 方案评估

由于 React Native CLI 环境配置比较繁琐,并且还容易受各种版本不配套的影响,容易把 PC 上的环境弄乱。

面对这种复杂的环境配置,身为技术宅,理所应当联想到,是否可以把这套环境打包在docker容器里面呢?

毕竟容器的网络是可以连通宿主机(默认的bridge模式)甚至跟宿主机共享同一网络(host模式)的,那宿主机能连的安卓设备,无论是物理机和虚拟机,也都有连接的可行性。

官方是否会提供已经配置好的容器镜像或者 Dockerfile 脚本呢?官方仓库里面没找到,但找到了一个看起来靠谱的 社区项目。这个社区项目有提供写好的 Dockerfile,并且在 dockerhub 上面也已经上传了现成的镜像。

那么策略就清晰了,先拉取构建好的镜像试试能不能编出包,连到安卓设备上。如果以后有什么环境版本上面的特殊需求,再基于基于他们的 Dockerfile 进行修改,自己构建镜像。

那么,先考虑下如何在 windows 电脑上面使用 docker:

  1. docker desktop
  2. minikube
  3. 用虚拟机软件启动一个 linux 虚拟机,在虚拟机中安装 docker
  4. 购买 windows 专业版,在 WSL 中安装 docker/k8s 使用

这里选的是最后一种方案,理由:

  1. WSL 和沙盒都是程序员常用的功能,家里的 windows 电脑为了使用这两个功能都已经买过专业版了。而 docker desktop 和 minikube 所使用的虚拟化技术可能跟 hyper-v 冲突。
  2. WSL 和宿主系统默认就是共享电脑磁盘的,我们可以在宿主机的 IDE 中编辑代码,在 WSL 中编译,使用起来也会比较方便。

这里从 WSL 的安装开始,把完整步骤全部写出来。

5.2 安装配置 WSL

使用管理员身份运行cmd

wsl --install -d Ubuntu-20.04

等一段时间之后,需要的资源都下载完成,就可以启动 WSL 了

wsl -u root

直接使用 root 用户操作 linux 并不是一个好习惯。正常来说应该使用普通用户并做好权限限制。这里使用 root 用户操作,一方面是为了简化教程,另一方面是这里的 WSL 只作为一个开发环境,也不涉及下载什么危险软件,就不讲究这个了。

习惯上,使用 linux 系统之前,先换上一个靠谱的国内软件源

echo "
deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse

deb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse

deb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse

deb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse

deb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse
" > /etc/apt/sources.list

改完配置文件后,需要执行一把更新,拉取软件源的最新列表

apt update

如果报错 DNS 无法解析阿里云的域名,就改一下 DNS 服务器地址。

# 每次重新进入 WSL,resolv.conf文件都会被覆盖写入,填成宿主机的ip
# 暂时没空定位是什么原因了,直接简单粗暴一点,让用户每次登录的时候都重新覆盖掉
# 下次重新进入 WSL 就不用执行这个操作了,会自动执行
echo "echo nameserver 223.5.5.5 > /etc/resolv.conf" >> /etc/profile
source /etc/profile

5.3 准备好 docker 容器

按照阿里云官网给的这个教程,安装docker-ce

apt update

apt -y install apt-transport-https ca-certificates curl software-properties-common

curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -

add-apt-repository "deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"

apt -y update

apt -y install docker-ce

启动 docker 服务

# 下次重新进入 WSL 就不用执行这个操作了,会自动执行
# 由于在 WSL 中用不了systemd 所以只能用这种方式来实现 docker 自启
# 这同样是个不太优雅的做法,因为 WSL 未关机的情况下重复登录进来会导致命令被重复执行。
# 但重复执行 start 命令不会重复拉起docker服务,对我们的使用没有什么影响,凑合用吧
echo "service docker start" >> /etc/profile
source /etc/profile

拉取镜像

docker pull reactnativecommunity/react-native-android

由于 dockerhub 是国外网站,所以速度较慢,近 3 个 G 的内容下载起来需要一定时间。所以也可以可以使用加速地址,从国内的 docker 源下载。但如果这么做,之后操作镜像的时候,镜像名称就要记得加上前缀。以下面的命令为例,镜像名称会变成 hub-mirror.c.163.com/reactnativecommunity/react-native-android

# 用这种方式拉取镜像也可以
docker pull hub-mirror.c.163.com/reactnativecommunity/react-native-android

启动容器

# 先在D盘创建一个目录,后面的RN工程都防止这里,windows 宿主机、WSL、容器三者都可以共享这个目录
mkdir /mnt/d/rn-projects

docker run -itd --name rn-container --network host -v /mnt/d/rn-projects:/root/rn-projects reactnativecommunity/react-native-android /bin/bash

5.4 尝试创建一个工程运行

docker exec -it rn-container bash

# 后面的环节会有查看 ip 的环节,并可能面临检查网络连通性的需求。安装这两个软件是为了使用 ifconfig 和 ping 命令。
apt update
apt install net-tools iputils-ping 

cd /root/rn-projects
npx react-native init AwesomeProject
cd AwesomeProject
npx react-native run-android

报错说连不上模拟器。因为此时电脑上面没有装任何安卓模拟器,也没对接任何真机:

error Failed to launch emulator. Reason: No emulators found as an output of `emulator -list-avds`.
warn Please launch an emulator manually or connect a device. Otherwise app may fail to launch.

现在可以关闭命令行窗口接着下面的步骤了。

5.5 安装模拟器

安装模拟器也有多种方案可以选择:

  1. 使用 WSA(Windows Subsystem for Android)
  2. 安装 android studio 使用它自带的模拟器
  3. 安装一个网上常见的安卓模拟器(平时主要用来打游戏的那种 xx 模拟器)

选择方案2,因为 WSA 目前尚未成熟,也无法很方便地在 windows 商店下载安装,而另外下载一款模拟器又嫌多此一举,毕竟 android studio 后面可能还会用到,而且支持的系统版本和机型也更多一些。

首先就是下载 android studio 进行安装。安装步骤就比较简单了,直接一路next。记得勾选一下安装虚拟设备的选项就行。

截图10.png

完成之后桌面图标启动 android studio。启动之后不要创建任何工程,因为我们这样子搭环境的话,不需要android studio来管理我们的工程,只需要它的模拟器即可。

剩下的配置步骤,跟上面的 4.2 章节一模一样,就不重复贴图了。

由于需要在宿主机使用 adb 命令,为了方便使用,我们把 android sdk 给配到环境变量里面,这样我们就可以使用它带有的 adb.exe 了。

添加一个环境变量叫做 ANDROID_HOME,值设置为:%LOCALAPPDATA%\Android\Sdk

在Path环境变量里面添加一个路径,值设置为:%ANDROID_HOME%\platform-tools

5.6 对接 docker 容器和安卓模拟器

将容器中的 adb 服务与物理机的模拟器对接上。

查询一下自己宿主机的ip

ipconfig

我这里查到我宿主机的 ip 是 192.168.31.103。

使用管理员权限运行 cmd,配置一个端口转发,把 WSL 的 adb 会话转发到宿主机的 127.0.0.1:5555 上面。

# 转发配置
netsh interface portproxy add v4tov4 listenport=5555 listenaddress=192.168.31.103 connectport=5555 connectaddress=127.0.0.1

# 如果不需要这个配置了,可以用这个命令删除
# netsh interface portproxy delete v4tov4 listenport=5555 listenaddress=192.168.31.103

配置完成之后要把防火墙关掉或者放行 5555 端口,否则无法在容器中连接 windows 宿主机的 5555。

进入容器

wsl -u root
service docker start
docker start rn-container
docker exec -it rn-container bash

在容器中调用 adb 进行连接宿主机模拟器。

adb connect 192.168.31.103:5555 # 提示 connected to 192.168.31.103:5555 即为连接成功
adb devices # 显示 192.168.31.103:5555     device 即为连接成功

在容器中启动 Metro 服务(这个窗口持续开着不要关掉,这里称为 1 号窗口)。

cd /root/rn-projects/AwesomeProject
npx react-native start --port 1234

另起一个窗口(这里称为 2 号窗口),在容器中调用模拟器启动 app。

npx react-native run android

此时在模拟器中必定会有红色报错,页面无法正常加载。需要调出开发者菜单,调一个东西。

两种调出方式:

  1. 鼠标点一下模拟器屏幕,按下 crtl + m 组合键。
  2. 在 2 号窗口中,使用 adb shell input keyevent 82 命令,控制手机调出菜单。

点击 settings -> Debug server host & port for device

输入 <容器ip>:1234

在 2 号窗口中重新调用一次 npx react-native run android,模拟器就能正确加载出应用了。

截图226.png

6 参考资料

React Native 官网

React Native Init vs Expo 2022: What Are the Differences?

stackoverflow.com/questions/5…

【React Native开发】React Native应用设备运行(Running)以及调试(Debugging)(3)