从零到一:用 React Native 构建我的第一个 APP 实战经验分享

技术森林
2025-06-14 23:15
阅读 745

开篇:为什么我会选择 React Native?

开篇:为什么我会选择 React Native?

我第一次接触 React Native 是在公司接了一个跨平台移动应用的需求。客户希望我们能在尽可能短的时间内,开发出同时支持 Android 和 iOS 的 App,并且预算和人手都比较紧张。

当时的我是个前后端工程师,对原生移动端开发并不熟悉。如果分别写两套代码,不仅效率低、维护成本高,而且学习曲线陡峭。就在这种情况下,团队里一位老哥建议试试 React Native —— 一套代码双端运行、学习门槛低、社区活跃,听起来很理想。

于是我开启了 React Native 的探索之路。这篇文章记录的是我第一次上手 React Native 开发完整项目的过程,包括背景、挑战、技术选型、代码实现以及踩坑经历。希望通过这篇“真实经历”,能让你少走弯路,快速入门 React Native 开发。


问题描述:从“Hello World”到实战的距离有多远?

问题描述:从“Hello World”到实战的距离有多远?

刚开始学 React Native 的时候,网上有很多 Hello World 级别的教程,比如用 react-native init 创建项目,写个按钮点一下弹个提示框之类的。但我真正开始做项目时才发现:

  • 真实场景中需要考虑布局适配的问题(横竖屏、不同机型);
  • 需要处理复杂的页面跳转、状态管理;
  • 要对接后端接口,处理用户登录、数据展示;
  • 还要考虑性能优化、打包发布等等……

而这些内容,很多教程都没有覆盖,甚至连文档都没讲得太清楚。

那会儿我遇到的最大困难是:如何从一个简单的 Demo 快速搭建出一个具备上线标准的 App?


解决方案:技术选型与实现思路

我们的项目是一个企业内部使用的工单系统,功能主要包括登录、任务列表、详情页、拍照上传、本地缓存等。下面是我当时做的技术选型和实现思路:

技术栈

模块 技术选型 备注
主体框架 React Native 使用 Expo 可以加速开发,但最终我们还是选择了纯 RN 构建,以便更灵活定制
页面导航 React Navigation 配置简单、生态丰富
状态管理 Redux Toolkit + RTK Query 替代之前的 Redux + Redux-Saga,简化异步请求逻辑
网络请求 Axios / RTK Query 封装 统一接口调用方式
UI 组件库 react-native-paper + 自定义组件 Material Design 风格,开箱即用
图片处理 react-native-image-picker 兼容安卓和 iOS
本地存储 Async Storage + MMKV(后期优化) 初期用 AsyncStorage,后来为了性能换成 MMKV
应用打包 EAS Build(Expo)、Fastlane(CI/CD) 提升构建效率

核心模块拆解

我把整个 App 分成了以下几个模块:

  1. 认证流程(登录、鉴权)
  2. 首页任务列表展示
  3. 任务详情页面(图文混排 + 拍照上传)
  4. 离线数据缓存机制
  5. App 打包发布准备

代码实践:关键部分实现说明

下面我挑几个核心模块来演示实际开发中的关键代码片段。

1. 初始化项目

npx react-native init MyFirstRNApp
cd MyFirstRNApp
npm install

接着安装必要依赖:

npm install @react-navigation/native @react-navigation/native-stack
npm install react-redux @reduxjs/toolkit axios react-native-paper react-native-image-picker @react-native-async-storage/async-storage

别忘了为 iOS 做 pod 安装:

cd ios && pod install --repo-update
cd ..

2. 登录页 UI + 表单验证

这是个经典的登录界面,包含用户名、密码输入框和一个登录按钮。

// screens/LoginScreen.js
import React, { useState } from 'react';
import { View, TextInput, Button, StyleSheet } from 'react-native';
import { useLoginMutation } from '../services/authApi';

export default function LoginScreen({ navigation }) {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const [login] = useLoginMutation();

  const handleLogin = async () => {
    try {
      await login({ username, password }).unwrap();
      navigation.navigate('Home');
    } catch (err) {
      alert('登录失败,请重试');
    }
  };

  return (
    <View style={styles.container}>
      <TextInput placeholder="用户名" value={username} onChangeText={setUsername} style={styles.input} />
      <TextInput placeholder="密码" value={password} onChangeText={setPassword} secureTextEntry style={styles.input} />
      <Button title="登录" onPress={handleLogin} />
    </View>
  );
}


![用户体验设计-1](https://code-guide.oss.shanghai.autogptai.club/common/file/download?name=date2025061423/b05e0dac-7d0c-4593-aa95-896b0716de45.jpg)


const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    padding: 16,
  },
  input: {
    marginBottom: 12,
    borderBottomWidth: 1,
    padding: 8,
  },
});

使用了 Redux Toolkit 的 createApi 来封装登录请求:

// services/authApi.js
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const authApi = createApi({
  reducerPath: 'authApi',
  baseQuery: fetchBaseQuery({ baseUrl: 'https://your-api.com/api/' }),
  endpoints: (builder) => ({
    login: builder.mutation({
      query: (credentials) => ({
        url: 'login',
        method: 'POST',
        body: credentials,
      }),
    }),
  }),
});

export const { useLoginMutation } = authApi;

3. 图片上传 + 本地预览功能

图片上传这部分其实也折腾了不少时间。主要原因是权限配置和路径兼容性问题。

// components/ImagePickerField.js
import React, { useState } from 'react';
import { Image, TouchableOpacity, Text } from 'react-native';
import * as ImagePicker from 'react-native-image-picker';

export default function ImagePickerField({ onImageSelected }) {
  const [imageUri, setImageUri] = useState(null);

  const pickImage = () => {
    ImagePicker.launchCamera(
      {
        mediaType: 'photo',
        includeBase64: false,
      },
      (response) => {
        if (response.didCancel) {
          console.log('用户取消了');
        } else if (response.errorCode) {
          alert('图片选择失败', response.errorMessage);
        } else {
          const uri = response.assets[0].uri;
          setImageUri(uri);
          onImageSelected(uri);
        }
      }
    );
  };

  return (
    <TouchableOpacity onPress={pickImage} style={{ padding: 16 }}>
      {imageUri ? (
        <Image source={{ uri: imageUri }} style={{ width: 200, height: 200 }} />
      ) : (
        <Text>点击拍照上传</Text>
      )}
    </TouchableOpacity>
  );
}

别忘了在 AndroidManifest.xml 添加相机权限哦:

<uses-permission android:name="android.permission.CAMERA"/>

4. Redux 数据缓存(使用 RTK Query)

RTK Query 的自动缓存机制非常方便,尤其在处理刷新、加载状态等方面特别实用。

// services/tasksApi.js
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const tasksApi = createApi({
  reducerPath: 'tasksApi',
  baseQuery: fetchBaseQuery({ baseUrl: 'https://your-api.com/api/' }),
  tagTypes: ['Tasks'],
  endpoints: (builder) => ({
    getTasks: builder.query({
      query: () => 'tasks',
      providesTags: ['Tasks'],
    }),
    uploadImage: builder.mutation({
      query: ({ taskId, formData }) => ({
        url: `/task/${taskId}/upload`,
        method: 'POST',
        body: formData,
      }),
      invalidatesTags: ['Tasks'],
    }),
  }),
});

export const { useGetTasksQuery, useUploadImageMutation } = tasksApi;

踩坑经验:那些让我熬夜调试的瞬间

虽然 React Native 上手容易,但在实际开发过程中还是遇到了不少坑。这里总结几个比较常见的:

🐞 1. 样式不生效,UI 错位

React Native 的样式体系与 Web 有很大不同,很多 CSS 特性不支持。比如:

  • 不支持百分比宽度;
  • 默认布局是 Flex;
  • 不能像 HTML 那样随便嵌套 inline 和 block;
  • 像素单位不是 px,而是 dp/density-independent pixels;

解决方案:

  • 多用 <View>flexDirection 控制布局;
  • 推荐使用 react-native-paperNativeBase 提供的一致性组件;
  • 调试工具推荐 React DevTools + Flipper;

🐞 2. 图片路径错误,上传总是失败

这个问题主要是因为 RN 对文件路径的处理方式不同于 Web。特别是在 Android 上,有些 URI 是 content:// 类型的,不能直接用于上传。

解决方案:

  • 使用 react-native-fsrn-fetch-blob 转换路径;
  • 在 iOS 上可以尝试获取 assets-library://file:// 格式;
  • 后续改用 MMKV 做缓存的时候也注意编码格式;

🐞 3. 打包之后崩溃,但开发环境没问题

这个坑我当时差点放弃 RN。Dev 环境跑得飞起,结果一打生产包就闪退。

最后发现是因为启用了 Hermes 引擎,在某些项目中会出现 JS 引擎兼容性问题。

解决办法:

  • 升级 Hermes 版本或者暂时禁用它;
  • 查看日志 adb logcat 或者 Xcode Console;
  • 建议使用官方推荐的 Metro + Hermes 最新版本组合;

🐞 4. 打包时签名失败,发布 Google Play 失败

这是因为没正确配置签名证书。尤其是 Android,发布版必须有 release key。

解决方法:

  • 创建 keystore 文件;
  • 修改 android/app/build.gradle 中的 signingConfigs;
  • 通过命令行生成签名 APK:
cd android
./gradlew assembleRelease

效果总结:从需求落地到上线,收获满满

这个项目大概持续了一个多月,最终顺利上线到公司内部员工手机上。整个项目完成之后,我对 React Native 的能力有了更深的认识:

  • ✅ 开发效率确实比原生高,特别是对于业务原型或中轻度交互的 App;
  • ✅ 一次开发、双端运行大大降低了人力和维护成本;
  • ✅ 社区插件生态丰富,基本想要的功能都能找到;
  • ✅ 集成热更新(如 CodePush)可以让修复 bug 更加及时;
  • ❗ 当然,也有一些缺点,比如:复杂动画支持弱、原生 SDK 集成麻烦、首次加载性能较差等。

不过总体来说,React Native 依然是目前跨平台移动开发中最成熟、最值得尝试的技术之一。


经验分享:给刚刚入门的你的几点建议

如果你刚接触 React Native,或者正准备用它来做个项目,以下是我的一些小建议:

🔧 1. 学会“调试大法”

  • 使用 React DevTools 监控组件渲染情况;
  • 用 Flipper 看网络请求、数据库、内存占用;
  • Android 用 ADB、iOS 用 Xcode 日志排查 crash;

🔄 2. 合理选型,不要什么都往项目里塞

  • 开始不要太贪,先跑起来最重要;
  • 别一开始就用太多三方库,避免升级冲突;
  • 如果是小型项目,可以直接用 Context + UseReducer;

📱 3. 重视适配和用户体验

  • 用 Dimensions API 做响应式布局;
  • 注意 iPhone X 以上的刘海屏适配;
  • 图片懒加载、骨架屏、下拉刷新等体验细节要打磨;

🚀 4. 发布之前一定要测试真机

  • 模拟器只是参考,真机才是王道;
  • 注意不同型号手机的表现差异;
  • 提前设置好埋点和异常上报,避免线上事故;

🌍 5. 学会 CI/CD 工具

  • 推荐配合 EAS Build、Fastlane、GitHub Actions 实现自动化打包发布;
  • 可以节省大量的手动操作时间;
  • 也能提升交付的稳定性;

写在最后:关于未来的思考

React Native 在 Facebook 官方推动和 Meta 开源之下,近年来发展稳定。虽然 Flutter、SwiftUI、Jetpack Compose 等新兴框架崛起,但在国内依然有大量的企业和开发者在使用 React Native。

我个人认为,RN 依然是当下入门最快、生态最完善、上手最轻松的移动跨平台开发方案之一。只要选对技术方案、注重工程化管理和性能优化,完全可以在一线项目中独当一面。

希望这篇文章能帮你少走弯路,尽快做出属于自己的第一个 React Native App。别怕犯错,代码就是在不断试错中成长起来的 😄

如果你对这个项目感兴趣,或者想看看完整源码,欢迎留言交流,我可以把 Demo 开源出来一起探讨!


📌 作者备注:文章内容基于真实工作经历编写,涉及公司业务已脱敏。文中提到的解决方案可根据具体场景适当调整。

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝