React Native+expo跨端开发


官方文档:https://reactnative.dev/docs/getting-started
中文文档:https://rn.nodejs.cn/docs/getting-started
个人中心:https://expo.dev/accounts/leeken
expo:https://expo.dev/go
模拟器https://expo.dev/orbit

expo是一个框架,可以不用模拟器方便开发React Native

手机去应用商店搜索expo go,并安装

npx create-expo-app@latest 创建一个expo项目
npx create-expo-app@latest ./ 在当前目录创建一个expo项目
回答问题:
确定继续吗? y
应用程序名字?

npm install 安装依赖

登录

npx expo login

开发命令

npx expo-doctor 检查环境是否配置正确
npx expo start 运行app
npm run android 运行到andorid 需要模拟器或真机
npm run ios 需要mac安装模拟器或真机
npm run web 运行到web
npm run reset-project 执行脚本,拒绝于package.js中如何定义
npx expo install 包名 包名 手动安装功能组件包
npx expo install 安装所有功能组件包,推荐

一般模拟器访问本机的IP地址为:
exp://10.0.2.2:8081

expo常识

app目录存放视图tsx文件,每个视图文件夹都需要一个_layout.tsx文件,用来实现路由,导航结构,访问控制等

推荐组织架构

bash
duola/
├─ app/                       # 路由驱动的页面(Screen)——全部 *.tsx
│  ├─ _layout.tsx             # 顶层布局,放导航容器 / 全局 Provider
│  ├─ +not-found.tsx          # 404页
│  ├── (tabs)/                # Tab 底部导航页
│  │   ├── index.tsx          # (首页)复习页面
│  │   ├── cards.tsx          # 卡片库页面
│  │   ├── chat.tsx           # 聊天页
│  │   └── settings.tsx       # 设置页面
│  ├─ user/                   # 用户相关页面,登陆,注册,个人中心
│  └─ test/                   # 调试测试模块,正式发布时不引用
│     ├─ sqliteViewer.tsx     # sqlite浏览器
│     └─ ...其它测试项.tsx
│
├─ src/                       # 业务逻辑层(纯 TypeScript)
│  ├─ user/                   # 用户模块逻辑
│  ├─ store/                  # 全局状态(读写token等,读设置)
│  ├─ services/               # 与后端、第三方交互
│  ├─ utils/                  # 公共工具函数
│  ├─ database/               # expo-sqlite方法
│  ├─ test/                   # 测试模块逻辑部分,正式发布时不引用
│  └─ theme/                  # 主题 / 样式变量,UI 库定制
│
├─ assets/
│  ├─ images/                 # 静态图片
│  ├─ fonts/                  # 自定义字体
│  └─ icons/                  # SVG/PNG 图标
│
├─ hooks/                     # 跨模块共用的自定义 Hook(可选)
├─ env.ts                     # 对 dotenv 或 expo-constants 的封装
└─ package.json

常用功能组件

expo-camera 使用摄像头(拍照/录像) ✅ 是
expo-image-picker 从图库选择图片或拍照上传 ✅ 是
expo-location 获取用户位置、地理定位 ✅ 是
expo-permissions(已废弃) 用于请求权限(新版用 expo-modules-core) ✅ 是
expo-constants 获取常量信息,如应用版本、设备 ID 等 ❌ 否
expo-device 获取设备信息,如品牌、系统版本、类型 ❌ 否
expo-network 网络状态检测(在线/离线) ❌ 否
expo-sensors 加速度计、陀螺仪、磁力计、气压计等传感器数据 ❌ 否
expo-secure-store 安全地存储小量敏感数据,如 token、密码等 ❌ 否
expo-notifications 推送通知(本地和远程) ✅ 是
expo-sharing 调用系统分享功能 ✅ 是
expo-splash-screen 自定义启动页 ❌ 否
expo-status-bar 控制状态栏样式(颜色、显示隐藏等) ❌ 否
expo-linear-gradient 实现渐变背景 ❌ 否
expo-haptics 设备震动反馈(触感) ❌ 否
expo-auth-session 第三方 OAuth 登录(如 Google、Facebook) ✅ 是
expo-clipboard 复制粘贴功能 ❌ 否
expo-image 高性能图片组件(新版 Image) ❌ 否
expo-updates OTA 热更新(Expo Go 和裸包都可用) ❌ 否
expo-font 加载自定义字体 ❌ 否

辅助包

npm install wa-sqlite web中的sqlite
npm install date-fns 轻量日期处理库
npx expo install expo-sqlite expo下的sqlite组件

UI库

模板:https://github.com/obytes/react-native-template-obytes
UI库:https://callstack.github.io/react-native-paper/

tailwind:https://www.nativewind.dev/docs/getting-started/installation
推荐,可以使用tailwindcss写样式
npx rn-new@latest --nativewind

编译和构建

需要翻墙,不然会因为网络原因退出
EAS Build云构建
首先注册expo账号
npm install -g eas-cli
eas login登录
eas build:configure 初始化构建和配置,回答问题:y,然后选择平台
eas build -p android --profile preview 开始云端打包
回答问题,确定包名,然后等待上传构建即可

EAS 本地构建
mac或linux环境下才支持
安装必要的依赖:
Node.js/npm
Android SDK 和 NDK(adnroid需要)
fastlane (iOS需要)
CocoaPods (iOS需要)
npm install -g eas-cli 安装eas环境
eas login 第一次需登录
eas build --platform android --local 本地构建

android studio构建
npx expo prebuild --platform android --clean 生成原生android项目

启动 Android Studio打开项目根下的 android/ 文件夹(首次可能需下载依赖)

菜单 Build > Build Bundle(s)/APK(s) 里选择:Build APK(s)
编译好后,左上角的"android"选择Project Files视图,然后找到这个项目的"android">app>build>outputs>apk>outputs/apk/debug/app-debug.apk

android studio减少体积:

  • android\gradle.properties文件:JS_ENGINE=hermes expo5.2默认,reactNativeArchitectures=arm64-v8a
  • android\app\build.gradle文件:
java
defaultConfig {
   	//其它配置
       resConfigs "zh", "en"                   // 只保留简体中文和英文资源
       ndk {                                   // ➜ NDK 配置
           debugSymbolLevel 'none' // 精简 native 符号表
       }
   }
splits {
       abi {
           enable true           // 开启 ABI 拆分
           reset()
           include 'armeabi-v7a', 'arm64-v8a' // 只生成 32/64 位 ARM
           universalApk false    // 不生成大而全的 universal-apk
       }
   }
buildTypes {
       // 其它配置
       release {
           // Caution! In production, you need to generate your own keystore file.
           // see https://reactnative.dev/docs/signed-apk-android.
           // signingConfig true // 注释掉使用调试签名
           shrinkResources true // 移除未用资源
           minifyEnabled true  // 开启代码混淆&优化
           proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
           crunchPngs false // 禁用 PNG 压缩
       }
   }
  • 生成 release APK:./gradlew clean assembleRelease

减少安装包体积
npx expo install --fix 分析包体积
npm ls --depth=0 检查未使用依赖

消息闪现

npm i react-native-root-toast
https://www.npmjs.com/package/react-native-root-toast
要在入口ts文件中使用

ts
// app\_layout.tsx:
import { RootSiblingParent } from 'react-native-root-siblings';

return (
<RootSiblingParent>
//  其它app标签
</RootSiblingParent>
);

使用闪现

ts
import Toast from 'react-native-root-toast';

// 闪现默认配置项
const defaultToastOptions = {
    duration: Toast.durations.SHORT, // 持续时间
    position: -80, // 距离顶部80像素的位置
    hideOnPress: true, // 点击隐藏
};

/**
 * @description 显示普通消息提示
 * @param {string} message - 提示消息
 * @param {object} options - 自定义配置选项
 */
export function showToast(message: string,type_msg:string='info') {
    return Toast.show(message, type_msg==='err'?{
        ...defaultToastOptions,
        textColor: '#000', //黑色字体
		backgroundColor: '#FFA500', //橙色背景
		opacity: 1, // 透明度
    }:{
        ...defaultToastOptions,
        textColor: '#FFF',
    });
}

设备权限开启

app.json:

json
"expo": {
    "ios": {
      "infoPlist": {
        "NSMicrophoneUsageDescription": "此应用需要访问麦克风进行录音功能"
      }
    },
    "android": {
      "permissions": [
        "RECORD_AUDIO",
        "WRITE_EXTERNAL_STORAGE",
        "READ_EXTERNAL_STORAGE"
      ],
    },
  }

npx expo install expo-audio

app.json:

json
{
  "expo": {
    "plugins": [
      [
        "expo-audio",
        {
          "microphonePermission": "Allow $(PRODUCT_NAME) to access your microphone."
        }
      ]
    ]
  }
}

集成tailwindcss

RN版tailwind文档:
https://www.nativewind.dev/docs/getting-started/installation

二选一:
npx rn-new@latest --nativewind 安装一个初始expo+tailwind脚手架

或者已经有expo项目,则使用以下方法安装
npx expo install nativewind react-native-reanimated@~3.17.4 react-native-safe-area-context@5.4.0 必要依赖
npx expo install --dev tailwindcss@^3.4.17 prettier-plugin-tailwindcss@^0.5.11 仅开发时的依赖(区分安装不会打包进app)

准备项:
app.json: "expo": "web": "bundler": 必须是"metro"

npx tailwindcss init 初始化,会创建tailwind.config.js文件

编辑tailwind.config.js:

js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./app/**/*.{ts,tsx}"], // app目录下的tsx才可能包含界面样式
  presets: [require("nativewind/preset")],
  theme: {
    extend: {},
  },
  plugins: [],
}

项目根目录创建一个global.css

css
@tailwind base;
@tailwind components;
@tailwind utilities;

根目录添加babel.config.js:

js
module.exports = function (api) {
  api.cache(true);
  return {
    presets: [
      ["babel-preset-expo", { jsxImportSource: "nativewind" }],
      "nativewind/babel",
    ],
  };
};

修改metro.config.js:

js
const { withNativeWind } = require('nativewind/metro'); //添加
//其它配置
module.exports = withNativeWind(config, { input: './global.css' }) //修改

根目录创建创建nativewind-env.d.ts(可选)
/// <reference types="nativewind/types" />

修改tsconfig.json:(官方没说)

json
` "include": [
    // 添加
    "nativewind-env.d.ts"
  ]

如果使用expo的路由在app/_layout.tsx 文件中导入
import '../global.css';
如果不是expo路由则可能是index.js

我遇到不生效的问题,编辑一下组件文字显示生效了

expo 错误疑难杂症

如果异步连接sqlite出现未关闭问题,使用下面这个,每次使用都重新连接:待验证

js
const db = await SQLite.openDatabaseAsync(databaseFile, {
  useNewConnection: true
});