☰
React Native + TypeScript + Expo:移动端混合开发选型与实战
2026/10/2 3:20:56 网站建设 项目流程

我做了三年移动端混合开发,从早期盲目跟风选型,到后面被线上问题逼着重新审视技术栈,最后在 React Native + TypeScript + Expo 这条组合上稳定下来。如果你正站在同样的岔路口——纠结要不要入 RN 的坑,或者已经被 Flutter、原生、各种小程序跨端方案搞得眼花缭乱,这篇文章就是为你写的。

这个系列会以 [RN + TS + Expo 移动端混合开发] 为基线,从技术选型逻辑讲起,一路覆盖开发环境搭建、工程架构、性能优化和实际打包上线环节。本篇先解决最核心的问题:为什么是 React Native,而不是别的?我会把这几年的真实踩坑经历、选型对比和底层判断逻辑全部摊开说,绝不只给你一份" RN 更好"的口号式结论。

1. 先把结论放在前面:React Native 适合谁,不适合谁

1.1 我的业务形态决定了我需要什么

很多人在选型时会犯一个致命错误:先把所有跨端方案拉出来,列一张对比表,然后凭技术热度或招聘市场的价格拍板。我一开始也是这样,但后来发现,真正能导向正确决策的起点,是你的业务形态和团队构成。

我当时的处境很典型:团队不足十人,没有专门的原生开发岗位,服务器端以 Node.js 为主,前端以 React/Next.js 为主,但同时要交付 iOS 和 Android 两个 App。这就引出了一个核心矛盾——如果选原生双端开发,意味着团队要同时维护两套代码、两套发布节奏、两套 UI 规范,而当时的产品原型验证尚未完成,经常要在一周内迭代三四个版本。这种情况下,双原生几乎是灾难。

如果选一个跨平台技术栈,需要的能力就非常明确了:

  • 团队成员必须能快速上手,已会的技能不能浪费
  • 热更新和快速迭代能力必须强,尽可能绕开应用市场上架的等待周期
  • 在未来某个阶段,仍然能够触及底层的原生能力,而不是困在框架的笼子里

React Native 恰好卡在这个需求点上。它允许移动端 UI 层用 JavaScript/TypeScript 编写,底层仍然通过桥接或新架构的 JSI 层调用原生组件和原生模块。这意味着,当你真的遇到了" React Native 做不了"的功能时,可以直接写原生代码并暴露给 JS 层调用,这条路始终是通的。

1.2 先搞清楚"混合开发"这个词的分量

"混合开发"在市面上经常被滥用,很多人把它等同于" WebView 套壳"。但 RN 的玩法完全不同:它不是把网页塞进壳里,而是通过 React 的声明式 UI 描述,映射到真实的平台原生组件。你在 JS 里写一个<View>,最终渲染给用户的是 Android 的 ViewGroup 或 iOS 的 UIView,而不是一个性能天然受限的 WebView 页面。

这个导向有两个直接好处:一是页面滑动流畅度、启动内存占用这些"手感指标"远优于 WebView 套壳方案;二是在调试工具链上,你依然可以享受 Chrome DevTools 或 React DevTools 带来的开发体验,而不是像原生开发那样重新建立一整套心智模型。

说句实在话,很多人对 RN 的第一印象停留在"体验一般、包体积大、启动白屏"这些抱怨上。这些问题确实存在,但大多集中在 RN 旧架构的老版本阶段,或者说,是工程配置没做到位的结果。新架构推出后,Fabric 渲染器和 TurboModule 替换了老旧的 Bridge,很多历史痛点已经从根上缓解了。这个问题我会在后面的启动白屏专题里用一整节展开讲。

2. 为什么不是 Flutter,也不是纯 Web 套壳

2.1 Flutter 与 React Native 的底层差异决定了不同走向

既然要回答"为什么选 React Native",绕不开 Flutter 这个最直接的对标。我不打算用一张所谓"社区活跃度对比图"来搪塞,而是尽量把底层差异讲的直白一点。

Flutter 走的是自绘引擎路线。它不依赖平台的 OEM 原生组件,而是用 Skia 引擎直接在 Canvas 上画出 UI。这意味着 Flutter 几乎不受平台 UI 能力限制,每个像素都可以自定义,渲染性能在跨端方案里是最稳定的那一档。但代价是:你的 UI 和 JavaScript 生态完全隔离,业务逻辑用 Dart 编写,与前端社区积累的 TypeScript 类型定义、组件库、工具链没有任何直接复用关系。

React Native 恰好相反。它依然依赖 iOS 和 Android 的原生组件去做最终渲染,因此你能得到接近原生的"平台质感";缺点也来源于此——平台差异需要时刻留意,某些 RN 组件在 Android 和 iOS 上的表现会有细微差异。

对于我这个团队来说,这个差异很快演变成了决策分岔路:

  • 如果团队需要最极致的跨端一致性,接受用 Dart 重新学一套语言和生态,Flutter 没问题。
  • 如果团队已经深度绑定 JavaScript/TypeScript 生态,并且希望未来能无缝接轨 React Web 业务,React Native 的复用价值是无价的。

我不否认 Flutter 技术本身优秀,但它解决的不是我团队当前的问题。技术选型不是选最强的,而是选最能撬动现有资源的。

2.2 React Native 在 Web 生态中的杠杆效应

React Native 有一个其他跨端方案很难复制的优势:它可以和 React Web 共享大部分业务逻辑。如果你的团队本来就是 React 技术栈,那 RN 的组件模型、状态管理方案、路由库、网络请求封装,几乎都能从现有代码基础上平移或者复用。

我在实际项目中做过一个统计:一个中等复杂度的订单模块,Web 端和 React Native 端可以复用的主要逻辑代码(非 UI 层)大约接近 60%,包括表单校验、数据状态管理、API 接口封装、通用工具函数。这不代表你能"一套代码全平台跑",但意味着你不需要为移动端单独培养一套开发语言、一套状态管理认知。类型定义可以共享,接口返回数据模型可以共享,团队之间的沟通成本会肉眼可见地降下来。

从成本角度算一笔粗账:一个 RN 工程师和一个 Flutter 工程师,在基础能力差不多的情况下,如果公司已经积累了 React 项目资产,前者的上手曲线短得多。而如果公司完全从零开始,没有 React 代码也没有前端团队,Flutter 的员工培训和 App 性能体验有时反而更平滑。所以你应该理解为什么我的结论是"适合谁"比"哪个好"更重要。

2.3 那些 Web 套壳方案看起来很美,但天花板明显

纯 WebView 套壳方案在早期迭代验证阶段确实很香——开发速度快到离谱,完全复用响应式网页,一套代码行走天下。但这种方案有不可避免的天花板:复杂的手势交互,比如地图拖拽、图片裁剪、富文本编辑器里的连续手势操作,在 WebView 里会频繁遭遇事件传递和渲染性能瓶颈;依赖原生能力的场景,比如推送、蓝牙、NFC,需要额外写桥插件,插件的质量和维护状态参差不齐。

混合开发的"混合"二字,衡量的恰恰是" Web 渲染与原生能力"融合的精确程度。RN 能优雅地做到"大部分 UI 交给原生组件,小部分复杂功能直接用原生实现",而 WebView 套壳的常见问题是:" Web 页面占大头,原生能力靠 bridge 插件一个接一个地缝补"。两者在长线演进上面的潜力差距,不是看一天的性能跑分就能看出来的。

3. TypeScript 在 React Native 项目里到底有多重要

3.1 没有类型约束的 RN,重构等于在雷区里裸奔

这一节单独把 TypeScript 拎出来讲,因为任何跳过 TypeScript 直接上手 RN 的念头,都是我强烈不推荐的。RN 组件之间通过 props 传递数据,业务层通过 Redux/Zustand 管理全局状态,网络层返回复杂嵌套的 JSON 结构,这种"到处都是数据流动"的架构,如果全用 JavaScript 默认的 any 类型放行,整个项目的隐性成本会高到你不愿意面对。

我见过不少 JavaScript 版本 RN 项目,早期开发确实快,但到了第三个迭代周期,需求开始频繁增加和调整 props 结构时,一个字段改动往往引发连锁崩溃,你根本不知道哪个页面还在依赖一个即将被删除的旧字段。当你看到 TypeError: Cannot read property 'xxx' of undefined 出现在某个用户反馈中,排查半天才发现是上一个组件的 props 拼错了对象名,那种感觉不是在写代码,是在拆盲盒。

TypeScript 的静态类型检查,把这一整类问题从事后排查提前到了编写阶段。只要组件的 props 接口定义严谨,内部状态流转的联合类型约束合理,大部分愚蠢的字段错误在编辑器里就会直接标红。

3.2 一份完整的 props 类型约定长什么样

在 RN 中,我通常会为每个组件单独建一个类型区域,必要时单独拆成 types.ts 文件。一个比较典型的写法是这样:

// src/components/OrderCard/types.ts import { ViewStyle, StyleProp } from 'react-native'; export interface OrderItem { id: string; orderNo: string; amount: number; status: 'pending' | 'paid' | 'shipped' | 'completed' | 'cancelled'; createdAt: string; } export interface OrderCardProps { data: OrderItem; onPress?: (order: OrderItem) => void; showAmount?: boolean; containerStyle?: StyleProp<ViewStyle>; }

这个接口一旦写清楚,任何调用方都能明确知道:orderStatus 不是随便填字符串,而是必须从那五个字面量中选择一个。未来如果要增加订单状态,TS 编译器会画出所有遗漏的 switch 分支,这种确定性能大幅度降低本地测试的人力消耗。

3.3 TypeScript 给自己人挖的几个小坑,提前说一下

第一,React Navigation 的路由参数类型建议统一抽到navigation.d.ts里集中管理,而不是在每个页面里写零散的useRoute类型断言,否则维护成本会迅速膨胀。

第二,第三方原生模块很多没有自带类型定义,这个时候不要直接declare module 'xxx'简单敷衍过去,最好自己写一份描述该模块 API 的d.ts,不然团队里每个人对模块的理解都会从零开始。

第三,fetch 或 axios 网络层是类型最容易丢失的重灾区。我建议封装一个泛型 API 方法,比如get<T>(url: string): Promise<T>,这样后端返回的数据永远自带结构定义,而不是你在业务代码里做一堆as any强转。

这些经验不是教科书上的规定,是我在真实项目里被反复教育出来的。RN 的核心竞争力在于快速迭代,而没有类型约束的快速迭代,只会在时间维度上累积技术债,最终必然反噬开发效率。

4. Expo 的价值不是省掉配置,而是替你隔绝了大量原生工程噪音

4.1 从 Expo Go 到 development build,我经历了三个阶段

很多 RN 公司还是用裸 RN CLI 起步,因为我最初也认为 Expo 是"教学玩具";后来被配置折磨了无数轮之后,我才发现自己对 Expo 的认知停留在它两三年前的状态,现在 Expo 体系已经完全不同了。

第一个阶段是纯 Expo Go。用手机扫码就能立刻预览,不用碰 Xcode、不用配 Android Studio,适合原型验证和 Demo 演示,五分钟把一个页面跑起来完全不是做梦。

第二阶段是 development build。当项目需要引入自定义原生模块,或者要开启一些敏感的权限配置时,虽然需要自己构建一次原生项目,但 Expo 的 prebuild 机制会自动生成ios/和android/原生工程,你可以在这份工程里自由修改原生代码,同时仍然保留 Expo 的便捷配置抽象。

第三阶段是持续集成和分发。Expo Application Services 提供云构建、自动签名、提交商店审核的能力,发布流程大大简化。相比之下,纯裸 RN 原生配置的 CI/CD 链路每个环节几乎都需要亲力亲为。

4.2 我为什么建议小团队优先拥抱 Expo

如果你的团队没有专职原生开发进行原生工程维护,裸 RN CLI 会让你陷入大量"与业务无关的配置泥潭"。你以为你在写业务代码,实际却花了大半天处理 CocoaPods 版本冲突、Android Gradle 下载失败、签名证书过期。Expo 的价值恰恰在于把这一层从"需要完全理解"变成"只需要理解边界在哪"。

但这不意味着你完全不需要理解原生——你依然要知道什么时候必须 eject 或使用 config plugin 去注入原生权限、模块和配置。只是这些污染业务重构的底层代码,不需要你在项目一启动时就全部掌握。这个"边界感"对于小团队极其宝贵。

这里也顺带提一个常见误区:很多人担心"用了 Expo 就拿不到原生功能"。实际不是这样。Expo 官方 SDK 涵盖相机、定位、推送、文件系统、本地数据库、生物认证等高频能力,用expo install统一管理版本,天然规避了很多第三方原生模块之间的版本冲突。真正缺失的冷门原生功能,你可以通过 expo-dev-client 和 config plugin 体系自己接入,并不会把你锁死。

4.3 Expo 对 TypeScript 的支持是目前所有 RN 路线中最舒服的

如果你创建新项目,npx create-expo-app默认模板不但内置 TypeScript 配置,还帮你把路径别名、ESLint、格式化工具全部初始化了一遍。对比裸 RN CLI 每次都要手动改tsconfig.json的路径映射,补一堆 ESLint 插件,Expo 的开箱即用体验真的帮团队省下了至少一个完整工作日的工程模板搭建时间。

5. 关于启动白屏问题:选 RN 之前就要了解的系统级判断

5.1 启动白屏到底是怎么来的

"React Native 启动白屏"是社区高频搜索词,也是很多项目在早期阶段最容易遭受质疑的地方。这个问题的本质并不复杂:App 启动时,原生部分要先完成初始化,创建 Application、加载原生模块、启动 JS 运行环境(JavaScriptCore 或 Hermes)、下载或加载 JS Bundle,在这个过程中用户看到的是一个完全空白的窗口,也就是俗称的"启动白屏"。

在旧架构下,JS Bundle 体积很大时,初始化、解析和执行时间会明显拉长,白屏持续时间就可能让人烦躁。但这并不代表 React Native 天生有原罪,白屏的严重程度与下面几件事高度相关:

  • JS Bundle 是否过大:未做拆包的初始化源码全都塞在一个主包,自然加载慢
  • Hermes 是否启用:Hermes 是 Meta 专门为 RN 打造的 JavaScript 引擎,其预编译 AOT 机制可以显著缩短启动解析时间
  • 原生侧启动屏(Splash Screen)是否能做到无缝衔接:很多白屏问题不是"启动慢",而是"启动屏消失后新页面渲染未完成"的空窗期

5.2 实际优化手段的优先级清单

我自己处理过的白屏优化,大致按这个优先级推进:

  1. 开启 Hermes,这是成本最低、收益最明显的优化项。
  2. 精简依赖库,把仅用于个别页面的大模块做动态按需加载。
  3. 使用 Metro 的unstable_enablePackageExports或拆分 Bundle 的能力,减少首屏初始化代码量。
  4. 在原生侧改造启动屏,让 Splash 页面停留时间覆盖到 RN 首帧渲染完成,避免白屏闪跳。

如果你使用的是 Expo 的现代版本,默认配置已经帮你开启了 Hermes,基础体验比几年前要稳很多。剩下的问题更多是应用层加载逻辑的调优。这个主题后续我会专门开一篇来写,这里先不展开。

5.3 把"白屏"当成选型可接受的一种工程代价

需要强调的是,白屏问题不是"用 RN 才会遇到"。Flutter 有冷启动问题和 Skia 首帧渲染开销,原生开发同样有系统进程启动和完整首屏布局时间。问题不在于哪种技术完全没有等待时间,而在于你能不能理解它的耗时来源,并有能力去优化它。相对于 WebView 套壳方案动不动一个完整网页的加载 + JS 执行时间,RN 的启动优化空间和确定性通常更好。

6. 一套可以复制到下一份工作的选型决策模型

6.1 四层过滤网

既然已经掰开揉碎讲完了各类方案的利弊,我最后送你一套在实际工作中可以直接套用的决策模型,选型时一层层过滤,基本不会跑偏。

第一步,列团队技能清单。不要抽象地问"团队会什么",而是具体到语言栈、状态管理方案、基础设施文化。

第二步,列产品生命周期要求。MVP 验证期、快速增长期、成熟维护期,不同阶段的诉求差异巨大。你可以在 MVP 期用比较轻的方案快速试错,但必须确定后期能不能平滑迁移。

第三步,列原生能力边界。哪些功能需要调用系统 API,哪些功能依赖第三方原生 SDK,这些依赖的维护方式决定了你对跨端方案"原生控制力"下限的要求。

第四步,算人力成本账。选型不是"免费"的,你需要预估团队切换到这套栈的学习成本、第三方库的维护成本、性能问题出现后的排查成本,并把它们转化为工时。

6.2 一套适用于中小团队的落地方案

以我当前团队为例,把这个模型倒进去,结论如下:

  • 团队掌握 TypeScript 与 React
  • 产品处于快速迭代期,每周至少发版一次
  • 需要推送、地图、相机、地理位置等原生能力
  • 原生人力资源为零,后端高峰时段也无法支援移动端

最终技术栈稳定在:React Native + TypeScript + Expo(development build)+ Hermes 引擎

这套栈让前端团队成员在一个晚上就能跑通完整项目,两天内开始写业务代码;原生侧的定制需求全部通过 config plugin 封装进基础设施层,业务代码不感知原生语言差异。

如果你问"这个决定会永远对吗",我的答案是:任何技术选型都会过期,但决策模型的复用价值远高于单次结论。当未来某一天新的跨端方案出现时,你能用同一套框架快速完成评估,而不是跟着社区热度随波逐流。

7. 系列预告:后续你还会看到什么

本篇相当于战役前的地图绘制,让你先看清整个战场的形态。之后的系列文章我会继续围绕 [RN + TS + Expo] 逐层深入:从环境搭建和首个项目初始化讲起,再讨论组件设计模式、Hermes 和性能调优、原生模块接入、CI/CD 发布流程,以及上架 iOS App Store 和 Android 商店的具体避坑经验。

每一篇都会包含完整可运行的实战代码和我在真实开发中踩过的细节坑。如果你想跟着实战走,建议先把本文第一部分和第二部分看懂,再在本地把 Expo 跑通,接下来的系列你会有更强的共鸣。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询