[2023] 开发 RN(Expo) App 的技术小节

1,100 阅读6分钟

📢 上篇文章 是我在 2021 年左右发布的, 如今部分内容已经过时了, 恰逢年底, 正好在做一个开发 React Native(Expo 框架) 应用的总结.

技术选型

组件/功能

🔥 这里是笔者使用的在 React Naitve/JS/Expo 生态中最好的库/组件.
名称功能说明备注
docs.expo.dev/versions/la…获取用户设备本地化信息, 例如设备使用的语言, 设备使用的文字方向
docs.expo.dev/versions/la…扫码/从图片中识别条形码
docs.expo.dev/versions/la…读取和写入剪切板
docs.expo.dev/versions/la…唤醒系统的分享
docs.expo.dev/versions/la…对生物识别(指纹/FaceId/TouchID) 封装
docs.expo.dev/versions/la…对文件系统的封装, 例如获取缓存位置, 写入本地文件, 读取本地文件…如果你不使用 Expo, 更推荐使用 github.com/itinance/re…
docs.expo.dev/versions/la…图片选择器, 调用系统图片选择, 并且系统裁剪功能
docs.expo.dev/versions/la…图库选择器, 和上面不同的是, 它还支持选择视频, 以及读取图库, 基于这个库你可以开发一个自定义的图库选择器
docs.expo.dev/versions/la…实现 Intent 功能, 方便打开系统配置.当你想要开启 App 设置时很有用, 例如用户未授予 App 相机权限, 使用这个库可以直接跳转到 App 设置
docs.expo.dev/versions/la…实现创建通知/撤销通知/管理小红点(badge) 等通知相关的处理
docs.expo.dev/versions/la…获取 App 的信息, 例如包名/版本号等信息
docs.expo.dev/versions/la…打开系统浏览器当你想要打开一个超链接, 但是不想调用系统浏览器时可以使用这个库, 调用后会在 App 里面直接打开网页, 不用跳转第三方浏览器.
docs.expo.dev/versions/la…线性渐变实现
github.com/dohooo/reac…走马灯/轮播
github.com/fnando/i18n…实现 I18N笔者的架构有些复杂, 我更推荐 , 它可以更好的和 react 结合.
docs.expo.dev/guides/icon…github.com/oblador/rea… 的封装, 提供更多友好的 api
github.com/tokkozhin/r…生成二维码, 提供更多样式支持.
github.com/mrousavy/re…提供持久化存储支持, 类似浏览器中的 LocalStorage另一个替代品, 性能没那么好 docs.expo.dev/versions/la…
www.npmjs.com/package/rea…Webview 支持, 同时还支持双向通信, 注入 JS 代码…
github.com/react-nativ…弹出层实现
github.com/deanhet/rea…文字滚动/marquee/ticker
docs.expo.dev/tutorial/sc…将 React 节点绘制成图片, 相当于 React Native 的 html2canvas
docs.expo.dev/versions/la…获取设备的网络信息
github.com/numandev1/r…提供图片/视频/音频的压缩如果你有图片的需求, 请使用这个库, 而不是 expo-image-manipulator 因为 expo-image-manipulator 很差

基础组件

⚙ 这里列出由 React Native 社区精心维护, 为 React Native 查漏补缺, 完善 React Native 官方没有做好的基础功能的库/组件.
名称功能说明备注
github.com/wix-incubat…对于可滚动视图中, 弹出键盘的逻辑处理更多细节: juejin.cn/post/710678…
github.com/DylanVann/r…高性能的图片渲染组件, 可完美替代 RN 的 Image 组件另一个替代品 docs.expo.dev/versions/la…
github.com/margelo/rea…高性能的图表组件依赖 github.com/Shopify/rea…, 只要你有图表需求, 并且对性能要求比较高, 请选择这个库. 注意 skia 会显著增加包大小.
github.com/software-ma…实现 SVG 渲染很多库都依赖它, 无脑安装.
github.com/software-ma…高性能动画实现很多库都依赖它, 无脑安装.
github.com/software-ma…手势支持很多库都依赖它, 无脑安装.

Polyfill/垫片

🛠 这里是列出常见 Polyfill
名称功能说明备注
github.com/charpeni/re…提供 React Native URL 对象的实现
github.com/browserify/…提供 Nodejs stream 模块实现
github.com/sindresorhu…提供 Nodejs querystring 模块实现

Base64

在 RN 中, atobbtoa 是没有被引擎实现, 这就意味着开发者要自行实现或引入 Polyfill, 在 Google React Native base64 polyfill最热门的 Stackoverflow 回答贴 是建议开发者使用 base64 作为 Polyfill, 实际上这不是一个好主意, 特别是当你要处理加密货币相关的逻辑时, 性能过于羸弱, 这里强烈建议使用 react-native-quick-base64 作为 Polyfill, 它由 C++ 实现, 在我的测试中, 同一个函数, 使用 base64 要执行 ≈2000ms 而使用 react-native-quick-base64 时仅 ≈100ms 就能执行完成

Crypto

在 RN 中, global.crypto 是未实现的, 和 base64 一样, 它的 Polyfill 有很多(crypto-js, crypto-browserify), 如果对性能有要求, 请使用 react-native-quick-crypto

Buffer

在 RN 中, global.buffer 是未实现的, 和 base64 一样, 它的 Polyfill 有很多(buffer, safe-buffer), 如果对性能有要求, 请使用 react-native-buffer

应该自己实现组件

👉 在 App 开发中, 我们程序员(苦逼)往往要根据产品和 UI 完成最终样式, 这使得 App 不像传统的 Web 后台一样使用 Element/Antd 之类一把梭, 笔者也尝试使用 Navi Base/React Native Elements, 效果并不好, 所以还是要对基础组件进行具体的操作和样式封装, 这里笔者列出常见的组件的实现方法, 当然这部分主要面向于有开发经验的程序员, 如果你刚刚入门, 笔者建议使用 Navi Base/React Native Elements 组件库.

底部弹出框/Action Sheet: 使用 react-native-modal + react-native-gesture-handler 封装

CheckBox/CheckBoxGroup: 参考 react-native-checkbox

LightBox/图片预览: 使用 react-native-modal + react-native-reanimated-carousel 封装

我弃用了哪些组件/库

名称原因替代品
react-native-async-storage.github.io/async-stora…性能原因github.com/mrousavy/re…
github.com/gorhom/reac…维护原因使用 github.com/react-nativ… + github.com/software-ma… 封装
github.com/expo/react-…维护原因使用 github.com/react-nativ… + github.com/software-ma… 封装
github.com/kcotias/rea…维护原因使用 github.com/react-nativ… + github.com/software-ma… 封装
tvke.github.io/react-nativ…可读性差使用传统 StyleSheet 代替
github.com/react-nativ…维护原因自行封装
github.com/arnnis/reac…维护原因自行封装
github.com/leecade/rea…性能和使用github.com/dohooo/reac…
github.com/awesomejerr…性能和更多样式支持github.com/tokkozhin/r…
node-libs-expo参考 polyfill 部分, 不在需要这个库
github.com/indiespirit…性能原因, 当我在页面上渲染超过五个图表时, 设备发热和卡顿异常明显.github.com/margelo/rea…

Monorepo

monorepo 在之前的文章中已经总结过, 并且内容也没有过时, 请参考: 基于 Expo 的 App 开发小计

热更新

开发基于 Expo 的 App 并搭建私有热更新服务

App (热)更新系统设计指南

注意事项/坑

  1. miui 的部分版本中, 文字可能无法完整显示, 这里推荐的做法是封装 Text 组件, 然后指定一个默认字体.
import { Text, TextProps } from "react-native";

export function YourText(props: TextProps) {
  return <Text
    {...props}
    allowFontScaling={false}
    style={[props.style, {fontFamily: props.style.fontFamily || "roboto"}]}
  />
}
  1. 如果你在使用 firebase-messagingexpo 时, 请注意在 Android 上通知图标可能是没有的, 这是因为如果你没有指定通知图标, Android 将使用你的应用图标作为通知图标, 但是大多数应用图标都不能作为通知图标(通知图标要求背景透明, 图标纯色), 更多细节可以看笔者为 react-native-firebasePR.
  2. 在 Expo 49 SDK 中, usesCleartextTraffic 由系统决定, 这个选项意味着应用程序是否可以访问 http 资源, 开发者可以通过 expo-build-propertites 配置.
[
  "expo-build-properties",
  {
    android: {
      // false 则不允许 http 资源
      usesCleartextTraffic: false,
    },
  },
];
  1. iOS 应用的审核要求用户拒绝授予某项权限后为用户提供一个友好的跳转, 例如: 用户点击拍照后拒绝授权相机, 开发人员必须提示 用户未授权 的字眼, 并且提供指向 设置 的按钮, 下面是一个简单的跳转到设置的实现.
import { Linking, Platform } from "react-native";
import * as IntentLauncher from "expo-intent-launcher";
import * as Application from "expo-application";

export namespace Camera {
  export const goAppSetting = () => {
    if (Platform.OS === 'ios') {
      Linking.openURL(`app-settings:`);
    } else {
      const activityAction = IntentLauncher.ActivityAction.APPLICATION_DETAILS_SETTINGS;
      const options = {
        data: `package:${Application.applicationId}`,
      };

      IntentLauncher.startActivityAsync(activityAction, options);
    }
  }
}
  1. @expo/vector-icons 不显示图标, 这个是一个相对冷门的问题, 在 monorepo 中, 如果在 App 仓库外引用 @expo/vector-icons 则会导致图标不显示, 解决方式时将所有涉及到 @expo/vector-icons 的代码移动到 App 里面, 笔者的复现方式如下
Root
  apps
    app1 // App
  shared
    组件库 // 如果在这里引用 @expo/vector-icons 就会导致图标无法显示