从零开始,用React Native搭建我的第一个APP

朱涛
2025-06-14 11:00
阅读 497

引言:为什么选择React Native?

引言:为什么选择React Native?

去年我加入了一家初创公司,负责快速开发一款跨平台的移动应用,目标是在三个月内推出iOS和Android版本。作为前端出身的我,虽然有过一些原生开发的经验,但面对时间紧迫、人手有限的情况,我们最终决定采用React Native

选择它的理由很直接:一次编码,两端运行。而且团队中大部分成员对React都有一定了解,学习成本相对较低。不过说实话,在真正上手之前,我对它的实际表现还是有些顾虑:比如性能、兼容性、与原生的交互是否顺畅。但现实没有给我们太多犹豫的时间,我们立刻开始了项目的搭建。

这篇文章就记录了我第一次使用React Native构建完整APP的经历:从项目结构到代码实现,再到踩坑经历和发布上线的心得。


背景介绍:我们的项目是什么?

背景介绍:我们的项目是什么?

我们做的是一个任务管理工具类App,用户可以通过创建待办事项、设置优先级、设定提醒等方式来提高工作效率。主要功能包括:

  • 用户登录/注册
  • 创建、编辑、删除任务
  • 日历视图展示任务
  • 基于本地通知的提醒
  • 离线支持(SQLite存储)
  • 适配深色模式和屏幕旋转

技术栈方面,后端用了Node.js + Express提供RESTful API,数据持久化层是PostgreSQL,移动端选择React Native + Expo(后期转成Eject以定制Native模块)。


遇到的挑战:从想法到落地并不容易

遇到的挑战:从想法到落地并不容易

刚上手的时候,我遇到的第一个问题是:“React Native到底应该怎么组织项目结构?”这听起来很简单,但在网上搜索时你会发现各种各样的做法,有的推荐按Feature划分目录,有的推崇Redux + Ducks模式,还有些是基于容器组件/展示组件分离的架构。

我尝试了几种方式后发现:保持简洁清晰才是王道,尤其是在中小型项目中,过度设计反而会带来维护负担。

另一个难点是性能优化和UI渲染效率。由于这个App有大量列表和动态内容更新,如果不注意组件重渲染或列表滚动优化,很容易卡顿。尤其在低端设备上,体验非常差。

此外,还有一些特定平台的问题需要处理:

  • iOS上的状态栏高度问题
  • Android上的权限申请(特别是通知权限)
  • 屏幕旋转时布局错乱
  • 打包后的体积过大

这些问题都需要逐一解决,并且需要兼顾两个平台的一致性。


解决方案:一步步搭建你的React Native APP

解决方案:一步步搭建你的React Native APP

移动端调试工具-2

技术选型决策

在项目初期,我们选择了Expo作为启动框架,因为它提供了很多开箱即用的功能,比如摄像头、相册、Push通知等插件,可以大大加快原型开发速度。

小贴士:如果你只是做一个小型PWA或者不涉及复杂原生逻辑的APP,建议一开始就用Expo;但如果需要深度定制Native模块,建议提前考虑是否需要“脱壳”(eject)

我们最终选择了中期阶段脱壳的方式,这样既保留了Expo的便利性,又能在后期接入更多定制化的原生能力。

项目结构规划

这是我最终采用的目录结构(简化版):

/src
├── assets             # 图片、字体资源
├── components         # 可复用的组件
├── screens            # 页面组件
├── services           # API请求服务
├── hooks              # 自定义Hook
├── store              # Redux相关(如果使用)
├── utils              # 工具函数
└── navigation         # 导航配置

移动设备适配-1

这种结构的好处是便于团队协作和维护,每个模块职责明确。比如screens文件夹里只放页面组件,components里放可复用的小部件。

核心模块划分

  1. 导航系统:使用React Navigation(Stack + Tab)
  2. 网络通信:封装了统一的API请求库,拦截错误码
  3. 状态管理:前期用Context API+useReducer过渡,后期改为Redux Toolkit
  4. 本地存储:采用SQLite进行离线数据缓存(通过expo-sqlite)
  5. 通知系统:使用Expo Notifications插件

开发流程实践

整个开发过程中,我们坚持几个原则:

  • 尽量使用纯JS组件避免不必要的Native依赖
  • 封装通用功能为Hooks,减少重复代码
  • 早期模拟接口数据,方便并行开发
  • 使用TypeScript提升代码健壮性

代码实践:关键模块示例

下面我会分享几个核心部分的代码片段,帮助你更快理解整体结构。

1. 初始化App结构

// App.tsx
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import MainStackNavigator from './src/navigation/MainStackNavigator';

const App = () => {
  return (
    <NavigationContainer>
      <MainStackNavigator />
    </NavigationContainer>
  );
};

export default App;

2. 导航结构定义(Stack)

// src/navigation/MainStackNavigator.tsx
import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import HomeScreen from '../screens/HomeScreen';
import TaskDetailScreen from '../screens/TaskDetailScreen';

const Stack = createStackNavigator();

const MainStackNavigator = () => {
  return (
    <Stack.Navigator initialRouteName="Home">
      <Stack.Screen name="Home" component={HomeScreen} />
      <Stack.Screen name="TaskDetail" component={TaskDetailScreen} />
    </Stack.Navigator>
  );
};

export default MainStackNavigator;

3. 接口请求封装示例

// src/services/api.ts
import axios from 'axios';

const apiClient = axios.create({
  baseURL: process.env.API_BASE_URL,
  timeout: 10000,
});

apiClient.interceptors.response.use(
  response => response,
  error => {
    if (error.response?.status === 401) {
      // 触发重新登录逻辑
    }
    return Promise.reject(error);
  }
);

export const fetchTasks = async () => {
  const res = await apiClient.get('/tasks');
  return res.data;
};

4. 本地数据库操作(SQLite)

// src/services/database.ts
import * as SQLite from 'expo-sqlite';

const db = SQLite.openDatabase('task.db');

export const initDB = () => {
  db.transaction(tx => {
    tx.executeSql(
      `CREATE TABLE IF NOT EXISTS tasks (
        id INTEGER PRIMARY KEY NOT NULL,
        title TEXT,
        completed BOOLEAN
      );`
    );
  });
};

export const getOfflineTasks = () => {
  return new Promise((resolve, reject) => {
    db.transaction(tx => {
      tx.executeSql(
        'SELECT * FROM tasks;',
        [],
        (_, result) => resolve(result.rows._array),
        (_, err) => reject(err)
      );
    });
  });
};

踩坑经验分享:那些让我熬夜的地方

1. 列表性能优化

我们在首页用了一个FlatList来展示任务列表。一开始没做任何优化,每次更新数据就会导致整个列表重新渲染,滑动非常卡顿。

后来加了这些改进:

  • 使用keyExtractor指定唯一Key
  • 组件加上React.memo()避免不必要的重渲染
  • 数据更新时尽量用Immutable方式避免引用污染
  • 对长列表进行了虚拟滚动优化(cell高度一致)

2. 屏幕旋转适配

某些页面在旋转屏幕后会出现布局错乱,尤其是Flex布局。我们用到了React Native的Dimensions模块监听变化,然后动态调整样式。

import { Dimensions } from 'react-native';

const onOrientationChange = () => {
  const { width, height } = Dimensions.get('window');
  setOrientation(width > height ? 'landscape' : 'portrait');
};

同时,在关键组件中使用条件判断控制显示效果,保证视觉一致性。

3. Push通知在iOS上无法弹出

调试了好几天才发现是因为苹果的APNs证书配置错误,而且必须手动开启推送权限(Expo默认不会自动请求)。这个问题在真机测试前根本看不出来。

解决方法是在入口处主动触发权限申请:

import * as Notifications from 'expo-notifications';

Notifications.requestPermissionsAsync();

另外还要确保后台服务器推送时携带正确的Token格式。

4. 安卓权限申请被拒绝

Android 6.0之后的所有危险权限都必须动态申请。我们在拍照上传头像时遇到权限被用户拒绝后无响应的问题。

于是写了个封装函数:

async function requestCameraPermission() {
  const { status } = await ImagePicker.requestCameraPermissionsAsync();
  if (status !== 'granted') {
    Alert.alert('权限不足', '请开启相机访问权限');
    return false;
  }
  return true;
}

5. 发布打包体积过大

首次使用Expo打包的产物竟然达到了48MB!远远超过苹果和谷歌商店推荐的限制。经过分析发现主要是第三方库和内置的一些冗余资产占用了空间。

我们采取了以下措施:

  • 移除不必要的插件(如未使用的传感器)
  • 替换部分UI库(如Ant Design Mobile RN版太大,改成了React Native Paper)
  • 压缩图片资源,用矢量图标替代PNG
  • 使用Proguard优化Android包体(仅适用于ejected项目)

最终包体缩小到27MB左右,符合主流审核标准。


效果总结:项目成果与收益

经过三个月的努力,这款任务管理App成功上线iOS和Android平台,首月下载量突破1万次,用户反馈良好。有几个关键指标值得参考:

指标 数值
平均启动时间 1.8秒
次留率(第3天) 35%
性能评分(Google Play Console) A级
包体大小(Android/iOS) 27MB / 32MB

最让我们欣慰的是,开发过程中积累的React Native经验和架构设计思维,成为后续多个项目的基石。团队内部也逐渐形成了自己的RN最佳实践指南


经验分享:给新手的几点建议

结合自己一路走来的经历,我想送给刚开始学习React Native的同学几点建议:

1. 学习路线要循序渐进

不要一开始就想搞多复杂的组件。先掌握基础三板斧:

  • JSX语法
  • State 和 Props
  • 生命周期和Hooks
  • 简单的导航跳转

然后逐步引入异步处理、本地存储、动画等内容。每一步都写点小Demo练手。

2. 实践比看文档更重要

文档再详细,不如亲手敲一遍代码来得实在。可以试着用React Native实现一个天气预报、记账本或Todo List,功能虽小但五脏俱全,是最好的练习材料。

3. 多关注平台差异

虽然React Native号称“Write once, run anywhere”,但实际开发时你会发现两个平台在细节上有不少差异,比如状态栏颜色、键盘行为、触摸反馈等。最好一开始就做好Platform判断,或者使用社区推荐的适配库(如react-native-safe-area-context)。

4. 重视性能与用户体验

性能问题往往是看不见摸不着的。建议尽早使用React DevTools Profiler和React Native Debugger工具进行调优。特别是在使用大量交互和动画时,更要小心内存泄漏和过度绘制。

5. 不要回避原生模块

当你遇到性能瓶颈或者需要用到专有硬件功能时(如指纹识别、AR),不可避免地要接触原生代码。这时候不要害怕去学Objective-C或Java。也可以使用现有的开源库,节省时间。


结语:一起拥抱跨端未来

React Native是一个不断进化的框架,尽管它现在还有很多不完善之处,但它依然是目前最容易上手、生态最完善的跨平台方案之一。

回过头来看,这段旅程虽然磕磕绊绊,但也正是一个个问题的解决,才让我真正理解了React Native的潜力与边界。无论你是前端转型移动端,还是准备入门前端工程师,都强烈建议你动手试一试,亲自感受一下跨端开发的魅力。

愿你在学习React Native的路上少踩坑、多收获!


如果你觉得这篇教程对你有帮助,欢迎在评论区留言讨论,或者分享你自己的React Native入门故事,我们一起成长!

评论 0

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