ReactNative 快速入门手册
1. 项目初始化
1.1. 创建新应用
如果之前安装了全局的 react-native-cli 包,请先将其卸载,以避免潜在的问题:
npm uninstall -g react-native-cli @react-native-community/cli
接下来,使用 React Native Community CLI 来生成一个新的项目。我们将创建一个名为 "MyApp" 的新 React Native 项目:
npx @react-native-community/cli@latest init MyApp
如果你是在现有应用中集成 React Native,或者在项目中安装了 Expo,或者为现有 React Native 项目添加 Android 支持,则不需要此步骤。你也可以使用第三方 CLI来设置 React Native 应用。
在初始化安装阶段,一定不要中断。
如果你在 iOS 上遇到问题,请尝试通过以下步骤重新安装依赖项:
cd ios # 进入 ios 文件夹
bundle install # 安装 Bundler
bundle exec pod install # 安装 CocoaPods 管理的 iOS 依赖
如果你想使用特定的 React Native 版本创建新项目,可以使用 --version 参数:
npx @react-native-community/cli@X.XX.X init MyApp --version X.XX.X
你也可以使用 --template 参数开始使用自定义的 React Native 模板,了解更多信息可以查看文档。
1.2. 启动 Metro
Metro 是 React Native 的 JavaScript 构建工具。要启动 Metro 开发服务器,请在项目文件夹中运行以下命令:
yarn start
注意:如果你熟悉 Web 开发,Metro 类似于 Vite 和 webpack 等打包工具,但专为 React Native 设计。例如,Metro 使用 Babel 将 JSX 等语法转换为可执行的 JavaScript。
1.3. 启动应用程序
让 Metro Bundler 在其自己的终端中运行。然后在你的 React Native 项目文件夹中打开一个新终端,运行以下命令
yarn android
如果一切设置正确,应该很快看到新应用在 Android 模拟器中运行。
这是一种程序的启动方法,你也可以直接通过 Android Studio 来运行。
1.4. 工程化说明
1. 项目结构
创建项目后,我们优化目录结构,如下:
MyApp/
├── android/ # Android 原生代码
├── ios/ # iOS 原生代码
├── node_modules/ # npm 依赖包
├── src/ # 应用源代码
│ ├── components/ # 可复用组件
│ ├── screens/ # 页面组件
│ ├── navigation/ # 路由导航
│ ├── assets/ # 静态资源(图片、字体等)
│ ├── services/ # 网络请求或其他服务
│ └── utils/ # 工具函数
├── App.js # 应用主入口
├── package.json # npm 配置文件
├── babel.config.js # Babel 配置
├── metro.config.js # Metro bundler 配置
└── index.js # React Native 入口文件
2. 重要文件与目录
-
android/: 包含 Android 原生代码和配置。你可以在这里修改 Android 特性、添加依赖或处理本地代码。
-
ios/: 包含 iOS 原生代码和配置。可以在这里修改 iOS 特性、添加依赖或处理本地代码。
-
src/: 应用的主要源代码目录。通常推荐将所有应用逻辑和组件放在这里,以便于管理和组织。
-
components/: 存放可复用的 UI 组件,通常是小的、独立的组件。
-
screens/: 应用的各个页面,通常是较大的组件,代表不同的视图或功能模块。
-
navigation/: 处理应用的路由和导航逻辑,通常会使用 @react-navigation/native 等库来管理导航。
-
-
assets/: 存放图片、字体和其他静态资源。
-
services/: 处理网络请求、API 调用或其他服务逻辑。
-
utils/: 存放通用的工具函数,例如格式化日期、处理字符串等。
-
App.js: 应用的主组件,是应用的入口点,通常在这里设置全局状态或根组件。
-
package.json: 定义项目的依赖和脚本。可以在这里添加或更新项目的 npm 包。
-
babel.config.js: Babel 的配置文件,用于转译 JavaScript 代码。
-
metro.config.js: Metro bundler 的配置文件,可以在这里自定义 bundler 行为,例如资源解析。
-
index.js: React Native 应用的入口文件,通常用于注册主组件并启动应用。
3. 项目配置
-
依赖管理: 使用 npm 或 yarn 管理项目依赖,可以在 package.json 中查看和添加依赖。
-
环境配置: 可以根据不同的环境需求在 ios 和 android 文件夹内配置项目设置,例如 API 地址、调试工具等。
-
样式: 可以使用 StyleSheet 来管理样式,或引入 styled-components 等库进行更灵活的样式管理。
1.5. 关于 Expo 框架
使用 Expo 确实可以简化开发,但是又会多一些学习成本,所以对于初学者来说,我建议不要使用任何框架,而是直接用原生 React-Native 来完成开发,这样对于 React-Native 的学习才能更深入。
Expo 链接:https://github.com/expo
2. 常用组件与 API
2.1. 常用组件
React Native 提供了多种内置组件,方便开发者快速构建用户界面。以下是一些常用组件及其用法。
1. View
用法:View 是 React Native 中最基本的组件,用于构建 UI 的布局。它可以容纳其他组件,并支持样式和事件处理。这个组件可以当做 div 用。
import { View } from 'react-native';const MyComponent = () => {return (<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>{/* 其他组件 */}</View>);
};
2. Text
用法:Text 组件用于显示文本。支持多种文本样式,如字体、颜色、大小等。
import { Text } from 'react-native';const MyText = () => {return (<Text style={{ fontSize: 20, color: 'blue' }}>Hello, React Native!</Text>);
};
3. Image
用法:Image 组件用于显示图片,支持本地图片和网络图片。
import { Image } from 'react-native';const MyImage = () => {return (<Imagesource={{ uri: 'https://example.com/image.png' }}style={{ width: 100, height: 100 }}/>);
};
4. ScrollView
用法:ScrollView 组件用于创建可滚动的视图,适用于内容较多的场景。
import { ScrollView, Text } from 'react-native';const MyScrollView = () => {return (<ScrollView><Text>Item 1</Text><Text>Item 2</Text><Text>Item 3</Text>{/* 更多内容 */}</ScrollView>);
};
5. TextInput
用法:TextInput 组件用于接收用户输入。可以设置占位符、样式等属性。
import { TextInput } from 'react-native';const MyTextInput = () => {return (<TextInputstyle={{ height: 40, borderColor: 'gray', borderWidth: 1 }}placeholder="Type here"/>);
};
6. Button
用法:Button 组件用于创建按钮,支持点击事件。
import { Button } from 'react-native';const MyButton = () => {return (<Buttontitle="Click Me"onPress={() => alert('Button pressed!')}/>);
};
7. FlatList
用法:FlatList 组件用于高效地渲染长列表,支持性能优化。
import { FlatList, Text } from 'react-native';const DATA = [{ id: '1', title: 'Item 1' },{ id: '2', title: 'Item 2' },{ id: '3', title: 'Item 3' },
];const MyFlatList = () => {return (<FlatListdata={DATA}renderItem={({ item }) => <Text>{item.title}</Text>}keyExtractor={item => item.id}/>);
};
8. TouchableOpacity
用法:TouchableOpacity 组件用于实现可点击的元素,支持透明度变化。
import { TouchableOpacity, Text } from 'react-native';const MyTouchable = () => {return (<TouchableOpacity onPress={() => alert('Tapped!')}><Text style={{ fontSize: 20 }}>Tap Me</Text></TouchableOpacity>);
};
9. Modal
用法:Modal 组件用于创建模态窗口,适用于需要用户注意的对话框。
import { Modal, View, Text, Button } from 'react-native';const MyModal = () => {const [modalVisible, setModalVisible] = useState(false);return (<View><Button title="Show Modal" onPress={() => setModalVisible(true)} /><Modaltransparent={true}visible={modalVisible}animationType="slide"><View style={{ marginTop: 50 }}><Text>This is a Modal!</Text><Button title="Hide Modal" onPress={() => setModalVisible(false)} /></View></Modal></View>);
};
10. SafeAreaView
用法:用于给页面给出安全区域的处理
<SafeAreaView style={backgroundStyle}><StatusBar />
</SafeAreaView>
2.2. 样式表
在 React Native 中,样式表的管理和应用非常重要。
React Native 提供了 StyleSheet API,方便开发者创建和管理组件的样式。以下是对 StyleSheet API 的详细介绍,包括主题的实现方案和 StyleSheet 的原理。
1. Stylesheet API
(1). 基本用法
StyleSheet API 允许开发者创建样式对象,使用 CSS 样式的语法来定义样式属性。这些样式对象可以直接应用到组件上,提高了样式的可读性和可维护性。
import { StyleSheet, View, Text } from 'react-native';const styles = StyleSheet.create({container: {flex: 1,justifyContent: 'center',alignItems: 'center',backgroundColor: '#f5fcff',},text: {fontSize: 20,color: 'blue',},
});const MyComponent = () => {return (<View style={styles.container}><Text style={styles.text}>Hello, React Native!</Text></View>);
};
(2). 性能优化
使用 StyleSheet.create 方法创建样式对象的一个主要好处是性能优化。React Native 会在内部对样式进行优化,避免在每次渲染时重新计算样式,从而提高性能。
2. 主题的实现方案
主题在应用中是一个重要的概念,允许开发者根据不同的环境(如浅色模式和深色模式)或用户偏好动态切换样式。以下是一些实现方案:
(1). 使用 Context API
可以使用 React 的 Context API 来创建主题上下文,允许在应用中轻松访问当前主题。
import React, { createContext, useContext, useState } from 'react';const ThemeContext = createContext();const ThemeProvider = ({ children }) => {const [theme, setTheme] = useState('light'); // 默认主题const toggleTheme = () => {setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));};return (<ThemeContext.Provider value={{ theme, toggleTheme }}>{children}</ThemeContext.Provider>);
};const useTheme = () => useContext(ThemeContext);
(2). 在组件中使用主题
通过 useTheme Hook 可以在组件中访问和使用当前主题。
const ThemedComponent = () => {const { theme } = useTheme();const styles = StyleSheet.create({container: {flex: 1,justifyContent: 'center',alignItems: 'center',backgroundColor: theme === 'light' ? '#fff' : '#333',},text: {color: theme === 'light' ? '#000' : '#fff',},});return (<View style={styles.container}><Text style={styles.text}>Current Theme: {theme}</Text></View>);
};
3. Stylesheet 的原理
React Native 的 StyleSheet API 处理样式的过程包括多个步骤,从样式的创建到应用于组件,涉及到性能优化、样式合并和跨平台支持等多个方面。以下是对这一过程的深入分析。
(1). 样式的创建
当你调用 StyleSheet.create 时,React Native 会进行以下操作:
-
样式对象的定义:你定义的样式对象被存储为一个不可变的对象。这意味着在样式创建后,它的属性不会被修改,从而提高了性能,因为 React Native 不需要在每次渲染时重新计算样式。
-
映射到原生代码:React Native 将这些样式对象映射到原生代码中。这是通过将 JavaScript 对象转换为原生视图的样式属性来实现的。这种转换使得 React Native 能够与 Android 和 iOS 的原生 UI 组件进行交互。
(2). 样式的存储与优化
在调用 StyleSheet.create 后,样式会被存储在内存中。React Native 维护一个样式表缓存,以避免在每次渲染时重新创建样式。具体过程如下:
-
缓存机制:样式表在应用中被缓存,React Native 会通过样式的 ID 来引用它们。这种机制确保了样式的快速访问。
-
样式的优化:在创建样式时,React Native 会分析每个样式的属性,决定哪些属性可以在原生端高效处理,哪些属性需要进行额外的处理。例如,flex、padding、margin 等布局相关属性会被优化为原生布局引擎可以直接使用的形式。
(3). 样式的应用
将样式应用到组件的过程相对复杂,涉及以下步骤:
-
组件渲染:当一个组件被渲染时,React Native 会检查其属性中的 style 属性。
-
样式合并:如果组件的 style 属性是一个数组,React Native 会将所有样式合并。合并过程中后面出现的样式会覆盖前面样式的同名属性。可以通过特定的方式将多个样式合并成一个对象,以便进行比较或传递。
const styles = [baseStyle, additionalStyle];
const combinedStyle = StyleSheet.flatten(styles);
- 应用样式到原生组件:合并后的样式通过桥接(Bridge)机制发送到原生层。React Native 使用 JSON 格式将这些样式传递给原生视图。
(4). 样式的更新与重新渲染
-
响应式更新:当组件的状态或属性变化时,React 会触发重新渲染。此时,它会重新计算需要应用的样式并根据新的状态合并样式。
-
原生与 JavaScript 的交互:React Native 通过异步的方式与原生代码进行交互,确保在样式更新时不会阻塞 UI 渲染。样式的变化通过消息传递机制推送到原生层,原生组件会在下一个渲染周期中更新样式。
(5). 跨平台支持
React Native 还提供了平台特定的样式支持。通过 Platform 模块,开发者可以为不同的平台定义特定的样式,这些样式在创建时被单独处理,以确保在 Android 和 iOS 上有最佳的表现。
import { Platform, StyleSheet } from 'react-native';const styles = StyleSheet.create({button: {backgroundColor: Platform.OS === 'ios' ? 'blue' : 'green',padding: 10,},
});
2.3. 路由处理
在 React Native 中,路由处理是应用导航的重要组成部分。
通过使用 @react-navigation 库,可以轻松地创建和管理不同的屏幕和导航结构。以下是对你提供的代码示例的详细讲解。
1. 项目依赖
在 package.json 中,你列出了几个与路由相关的依赖:
"@react-navigation/bottom-tabs": "6.6.1",
"@react-navigation/native": "6.1.18",
"@react-navigation/stack": "6.4.1",
"@react-navigation/native-stack": "6.11.0",
"react-native-screens": "3.35.0",
"react-native-safe-area-context": "4.14.0"
-
@react-navigation/native: 核心导航库,提供基本的导航功能。
-
@react-navigation/bottom-tabs: 用于创建底部标签导航的库。
-
@react-navigation/native-stack: 提供原生堆栈导航的实现,具有更好的性能。
-
react-native-screens: 优化应用性能的库,支持原生屏幕管理。
-
react-native-safe-area-context: 提供安全区域支持,确保在刘海屏或其他特殊屏幕形状下的内容显示正确。
2. 创建底部标签导航
在 src/routers/index.tsx 中,你首先创建了底部标签导航组件:
const TabBar = createBottomTabNavigator();function BottomBar() {return (<TabBar.NavigatorscreenOptions={{headerShown: false,}}><TabBar.Screenname="TabHome"component={HomeScreen}options={{tabBarLabel: '首页',}}/><TabBar.Screenname="TabOther"component={DetailScreen}options={{tabBarLabel: '其他',}}/></TabBar.Navigator>);
}
-
createBottomTabNavigator: 创建底部标签导航器。
-
TabBar.Navigator: 定义底部导航的容器。
-
TabBar.Screen: 定义每个标签页及其对应的组件。
headerShown: false 表示在底部标签导航中不显示头部。
3. 创建堆栈导航
接下来,创建一个堆栈导航组件,用于管理更复杂的导航结构:
const Stack = createNativeStackNavigator();function Navigator() {return (<NavigationContainer><Stack.NavigatorscreenOptions={{headerTitleAlign: 'center',headerBackTitleVisible: false,gestureEnabled: true,gestureDirection: 'horizontal',...Platform.select({android: {headerStatusBarHeight: StatusBar.currentHeight,},}),headerStyle: {backgroundColor: Colors.primary,},}}><Stack.Screen name="BottomBar" component={BottomBar} /><Stack.Screen name="Home" component={HomeScreen} /><Stack.Screen name="Details" component={DetailScreen} /><Stack.Screen name="Topics" component={TopicsScreen} /></Stack.Navigator></NavigationContainer>);
}
-
createNativeStackNavigator: 创建一个原生堆栈导航器。
-
NavigationContainer: 作为导航树的根容器,提供路由上下文。
-
Stack.Navigator: 定义堆栈导航的容器。
-
Stack.Screen: 定义堆栈中的每个屏幕及其对应的组件。
⚙️ 执行 pod 安装依赖:npx pod-install ios,如果不成功可能是网络问题,或者 进到 ios 文件夹,执行 pod install
如果是安装了原生内容,需要 pod 构建,我建议大家用 Xcode 打开。
4. 路由处理过程
-
导航结构:在此示例中,定义了一个底部标签导航(BottomBar)和多个堆栈屏幕(Home、Details、Topics)。用户可以在底部标签之间切换,同时也可以通过堆栈导航访问详细信息和主题。
-
屏幕的渲染:当用户切换到某个标签时,BottomBar 中的对应组件会被渲染。在这个底部导航中,HomeScreen 和 DetailScreen 是两个标签页。
-
堆栈导航:如果在 HomeScreen 中,我们希望导航到 DetailScreen,可以使用 navigation.navigate('Details') 方法来实现。这将把 DetailScreen 推入堆栈中,并自动管理返回操作。
-
状态管理:在导航中,React Navigation 会自动管理导航状态,例如当前活动的屏幕、参数传递等。每当导航状态变化时,相关的组件会重新渲染以反映新的状态。
5. 主题和样式
在 Navigator 函数中,你还通过 screenOptions 为导航器设置了一些样式和行为选项:
-
headerTitleAlign: 设置头部标题的对齐方式。
-
headerBackTitleVisible: 是否在返回按钮上显示标题。
-
gestureEnabled: 是否启用手势返回功能。
-
headerStyle: 设置头部的样式,例如背景颜色。
2.4. 常用API
1. AsyncStorage
用于本地存储简单数据,以键值对的形式存储。
import AsyncStorage from '@react-native-async-storage/async-storage';/// 存储数据
const storeData = async (value) => {try {await AsyncStorage.setItem('@storage_Key', value);} catch (e) {// 保存错误}
};// 读取数据
const getData = async () => {try {const value = await AsyncStorage.getItem('@storage_Key');if (value !== null) {// 值已存在}} catch (e) {// 读取错误}
};
2. Fetch API
用于进行网络请求,类似于浏览器的 fetch。
const fetchData = async () => {try {let response = await fetch('https://api.example.com/data');let json = await response.json();console.log(json);} catch (error) {console.error(error);}
};
3. Linking
用于处理外部链接,如打开网页或拨打电话。
import { Linking } from 'react-native';// 打开网页
const openURL = async (url) => {const supported = await Linking.canOpenURL(url);if (supported) {await Linking.openURL(url);}
};// 拨打电话
const makeCall = (phoneNumber) => {Linking.openURL(`tel:${phoneNumber}`);
};
4. DeviceInfo
获取设备信息,如型号、系统版本等,需安装 react-native-device-info
import DeviceInfo from 'react-native-device-info';const getDeviceInfo = async () => {const deviceId = await DeviceInfo.getDeviceId();const systemVersion = await DeviceInfo.getSystemVersion();console.log(`Device ID: ${deviceId}, System Version: ${systemVersion}`);
};
5. Permissions
请求和检查设备权限,需安装 react-native-permissions。
import { PermissionsAndroid } from 'react-native';const requestLocationPermission = async () => {try {const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,{title: '位置权限',message: '需要访问您的位置',buttonNeutral: '稍后询问',buttonNegative: '取消',buttonPositive: '确认',});if (granted === PermissionsAndroid.RESULTS.GRANTED) {console.log('您可以使用位置');} else {console.log('位置权限被拒绝');}} catch (err) {console.warn(err);}
};
6. Vibration
控制设备震动。
import { Vibration } from 'react-native';// 震动1000毫秒
const triggerVibration = () => {Vibration.vibrate(1000);
};
7. Share
用于分享内容到其他应用,如社交媒体。
import { Share } from 'react-native';const shareMessage = async () => {try {const result = await Share.share({message: '这是要分享的消息',});if (result.action === Share.sharedAction) {if (result.activityType) {// 共享到特定活动} else {// 仅共享}} else if (result.action === Share.dismissedAction) {// 共享被取消}} catch (error) {alert(error.message);}
};
8. StatusBar
控制状态栏的样式和行为。
import { StatusBar } from 'react-native';// 设置状态栏样式
const setStatusBar = () => {StatusBar.setBarStyle('light-content', true);StatusBar.setBackgroundColor('#000000');
};
3. 多端开发方案
在开发跨平台移动应用时,React Native 提供了一系列工具和策略,以便在 iOS 和 Android 之间实现良好的适配。这一适配思路不仅确保了应用的功能一致性,还能针对不同平台的特性优化用户体验。以下是具体的适配思路和方法:
3.1. 使用 Platform API
React Native 中的 Platform 模块是实现跨平台适配的基础。它提供了对当前平台的识别能力,使开发者能够根据不同平台调整组件的渲染和样式。
常用属性与方法:
-
Platform.OS:返回当前运行的操作系统,可以用于条件渲染。
-
Platform.select():根据当前平台返回适合的值,允许为每个平台定义不同的样式或组件。
import { Platform, StyleSheet } from 'react-native';const styles = StyleSheet.create({container: {flex: 1,...Platform.select({android: { backgroundColor: 'green' },ios: { backgroundColor: 'red' },default: { backgroundColor: 'blue' },}),},
});
这种方式使得代码更具可读性和可维护性,同时减少了条件语句的复杂性。
3.2. 利用 React Native 提供的常量
React Native 还提供了一些平台特定的常量,可以帮助开发者获取设备信息,从而优化适配。例如,使用 PlatformConstants(在最新版本中可能直接通过Platform或其他模块体现,此处为原文本内容)来获取设备的硬件和系统信息,这些信息可用于条件渲染或功能限制。
常用常量:
-
isPad:判断当前设备是否为 iPad。
-
forceTouchAvailable:判断当前设备是否支持 3D Touch(仅限 iOS)。
-
osVersion:获取当前操作系统的版本信息。
import { Platform } from 'react-native';if (Platform.isPad) {// 针对 iPad 的特定逻辑
}
3.3. 适配图片和资源
在处理多端时,图片资源的管理也非常重要。可以根据不同平台选择不同的图片资源,以确保最佳的加载和显示效果。
const imageSource = Platform.select({ios: require('./images/image_ios.png'),android: require('./images/image_android.png'),
});
3.4. 使用第三方库
许多第三方库提供了跨平台支持,使得开发者能够更轻松地实现平台适配。例如,使用 react-native-elements 或 react-native-paper 可以获取一致的 UI 组件,这些组件自动适配不同的平台。
3.5. 性能和体验优化
除了视觉适配外,性能和用户体验也是跨平台开发中不可忽视的部分。
可以根据不同平台的特性,调整动画效果、事件响应等。例如,Android 上的返回按钮行为可能与 iOS 的导航模式有所不同,这需要在逻辑上进行适配。
4. 原生模块开发与接入
你的应用可能需要一些平台功能,但这些功能并不是直接通过 React Native 或社区维护的众多第三方库提供的。你可能想要复用一些现有的 Objective-C、Swift、Java、Kotlin 或 C++ 代码。React Native 提供了一套强大的 API,帮助你将原生代码与 JavaScript 应用代码连接起来。
原生内容分为两部分:
-
原生模块:没有用户界面的原生库,比如持久存储、通知、网络事件等。这些功能可以作为 JavaScript 函数和对象供用户使用。
-
原生组件:可以通过 React 组件在你的应用的 JavaScript 代码中使用的原生视图、小部件和控制器。
在过往的开发中,我们统一使用 NativeModules,但是新的架构下,React-Native 抽象出了两个新概念用于使用与新架构下的原生模块与原生组件,分别为:
-
Turbo Native Module
-
Fabric Native Components
NativeModules 是已弃用的原生模块和组件 API。尽管如此,由于我们的互操作层,你仍然可以在新架构下使用很多旧版库。你可以考虑:
使用替代库;
升级到对新架构支持更好的新版本库;
将这些库自己移植为 Turbo 原生模块或 Fabric 原生组件。
原生组件的开发与接入请点击此处查看参考文档,本文中介绍原生模块的开发与接入。
4.1. 原生模块开发与接入 (iOS)
我们来实现 Web Storage API 的 localStorage。
为了让它在移动端工作,我们需要使用 Android 和 iOS 的 API:
-
Android: SharedPreferences
-
iOS: NSUserDefaults
1. 声明类型规范
React Native 提供了一个名为 Codegen 的工具,可以根据用 TypeScript 或 Flow 编写的规范生成适用于 Android 和 iOS 的平台特定代码。这个规范声明了在你的原生代码与 React Native JavaScript 运行时之间传递的方法和数据类型。Turbo 原生模块既是你的规范,也是你编写的原生代码,以及从规范生成的 Codegen 接口。
创建一个规范文件:在应用根目录下创建一个名为 specs 的新文件夹,文件夹里创建一个名为 NativeLocalStorage.ts 的新文件。
以下是 localStorage 规范的实现:
// specs/NativeLocalStorage.ts
import type {TurboModule} from 'react-native';
import {TurboModuleRegistry} from 'react-native';export interface Spec extends TurboModule {setItem(value: string, key: string): void;getItem(key: string): string | null;removeItem(key: string): void;clear(): void;
}export default TurboModuleRegistry.getEnforcing<Spec>('NativeLocalStorage') as Spec;
2. 配置 Codegen 运行
React Native Codegen 工具会根据规范生成平台特定的接口和样板代码。为此,Codegen 需要知道在哪里找到我们的规范以及该如何处理它。更新你的 package.json,如下:
{"scripts": {"start": "react-native start","test": "jest"},"codegenConfig": {"name": "NativeLocalStorageSpec","type": "modules","jsSrcsDir": "specs","android": {"javaPackageName": "com.nativelocalstorage"}},"dependencies": {...}
}
在将所有内容连接到 Codegen 后,我们需要准备我们的原生代码。
CocoaPods 生成的项目中,通过脚本自动运行 Codegen。
cd ios
bundle install
bundle exec pod install
输出内容大致如下:
...
Framework build type is static library
[Codegen] Adding script_phases to ReactCodegen.
[Codegen] Generating ./build/generated/ios/ReactCodegen.podspec.json
[Codegen] Analyzing /Users/me/src/TurboModuleExample/package.json
[Codegen] Searching for codegen-enabled libraries in the app.
[Codegen] Found TurboModuleExample
[Codegen] Searching for codegen-enabled libraries in the project dependencies.
[Codegen] Found react-native
...
3. 使用 Turbo 原生模块编写应用代码
使用 NativeLocalStorage,以下是修改后的 App.tsx,它包含了一些我们希望持久化的文本、一个输入字段和一些按钮来更新该值。
TurboModuleRegistry 支持两种获取 Turbo 原生模块的方式:
-
get<T>(name: string): T | null:如果 Turbo 原生模块不可用,则返回 null。
-
getEnforcing<T>(name: string): T:如果 Turbo 原生模块不可用,则抛出异常。假定该模块始终可用。
// App.tsx
import React from 'react';
import {SafeAreaView,StyleSheet,Text,TextInput,Button,
} from 'react-native';import NativeLocalStorage from './specs/NativeLocalStorage';const EMPTY = '<empty>';function App(): React.JSX.Element {const [value, setValue] = React.useState<string | null>(null);const [editingValue, setEditingValue] = React.useState<string | null>(null);React.useEffect(() => {const storedValue = NativeLocalStorage?.getItem('myKey');setValue(storedValue ?? '');}, []);function saveValue() {NativeLocalStorage?.setItem(editingValue ?? EMPTY, 'myKey');setValue(editingValue);}function clearAll() {NativeLocalStorage?.clear();setValue('');}function deleteValue() {NativeLocalStorage?.removeItem(editingValue ?? EMPTY);setValue('');}return (<SafeAreaView style={{flex: 1}}><Text style={styles.text}>当前存储的值是:{value ?? '无值'}</Text><TextInputplaceholder="请输入要存储的文本"style={styles.textInput}onChangeText={setEditingValue}/><Button title="保存" onPress={saveValue} /><Button title="删除" onPress={deleteValue} /><Button title="清空" onPress={clearAll} /></SafeAreaView>);
}const styles = StyleSheet.create({text: {margin: 10,fontSize: 20,},textInput: {margin: 10,height: 40,borderColor: 'black',borderWidth: 1,paddingLeft: 5,paddingRight: 5,borderRadius: 5,},
});export default App;
4. 编写原生平台代码
准备好后,我们将开始编写原生平台代码。这个过程分为两个部分:
注意:本指南展示的是如何创建一个仅适用于新架构的 Turbo 原生模块。如果你需要同时支持新架构和旧架构,请参阅我们的向后兼容性指南。
现在我们开始编写 iOS 平台代码,确保 localStorage 在应用关闭后仍然可用。
第一步:准备Xcode项目。
我们需要使用 Xcode 准备 iOS 项目。完成以下六个步骤后,你将拥有实现生成的 NativeLocalStorageSpec 接口的 RCTNativeLocalStorage。
(1). 打开 CocoaPods 生成的 Xcode 工作区。
cd ios
open TurboModuleExample.xcworkspace
如下图所示:

(2). 在 Xcode 工作区中,右键点击 app 目录,选择“新建组”,将新组命名为NativeLocalStorage。

(3). 在 NativeLocalStorage组中,选择“新建=>从模板创建文件”。

(4). 使用 Cocoa Touch Class 模板创建新文件。

(5). 将 Cocoa Touch Class 命名为 RCTNativeLocalStorage,并选择 Objective-C 语言。

(6). 将 RCTNativeLocalStorage.m重命名为 RCTNativeLocalStorage.mm,使其成为一个 Objective-C++ 文件。

第二步:实现 localStorage,使用 NSUserDefaults。
首先更新 RCTNativeLocalStorage.h:
// NativeLocalStorage/RCTNativeLocalStorage.h
#import <Foundation/Foundation.h>
#import <NativeLocalStorageSpec/NativeLocalStorageSpec.h>NS_ASSUME_NONNULL_BEGIN@interface RCTNativeLocalStorage : NSObject <NativeLocalStorageSpec>@endNS_ASSUME_NONNULL_END
然后更新我们的实现,使用带有自定义套件名称的 NSUserDefaults:
// NativeLocalStorage/RCTNativeLocalStorage.mm
#import "RCTNativeLocalStorage.h"static NSString *const RCTNativeLocalStorageKey = @"local-storage";@interface RCTNativeLocalStorage()
@property (strong, nonatomic) NSUserDefaults *localStorage;
@end@implementation RCTNativeLocalStorageRCT_EXPORT_MODULE(NativeLocalStorage)- (id)init {if (self = [super init]) {_localStorage = [[NSUserDefaults alloc] initWithSuiteName:RCTNativeLocalStorageKey];}return self;
}- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {return std::make_shared<facebook::react::NativeLocalStorageSpecJSI>(params);
}- (NSString * _Nullable)getItem:(NSString *)key {return [self.localStorage stringForKey:key];
}- (void)setItem:(NSString *)value key:(NSString *)key {[self.localStorage setObject:value forKey:key];
}- (void)removeItem:(NSString *)key {[self.localStorage removeObjectForKey:key];
}- (void)clear {NSDictionary *keys = [self.localStorage dictionaryRepresentation];for (NSString *key in keys) {[self removeItem:key];}
}@end
重要事项:RCT_EXPORT_MODULE 导出并注册模块,使用我们在 JavaScript 环境中访问它的标识符:NativeLocalStorage。更多详细信息请查看相关文档。
你可以使用 Xcode 跳转到 Codegen 的 @protocol NativeLocalStorageSpec,也可以用 Xcode 生成存根。
在模拟器上构建并运行代码:
yarn run ios
4.2. 原生模块开发与接入(Android)
React-Native 层的开发与 iOS 一致,我们从 Android 平台开发展开。
以下是针对 Android 部分的完整实现和优化:
现在,我们来实现 Android 平台代码,以确保 localStorage 在应用关闭后能够持久保存。
首先,实施生成的 NativeLocalStorageSpec 接口:
android/app/src/main/java/com/nativelocalstorage/NativeLocalStorageModule.java
package com.nativelocalstorage;import android.content.Context;
import android.content.SharedPreferences;
import com.nativelocalstorage.NativeLocalStorageSpec;
import com.facebook.react.bridge.ReactApplicationContext;public class NativeLocalStorageModule extends NativeLocalStorageSpec {private static final String NAME = "NativeLocalStorage";public NativeLocalStorageModule(ReactApplicationContext reactContext) {super(reactContext);}@Overridepublic String getName() {return NAME;}@Overridepublic void setItem(String value, String key) {SharedPreferences sharedPref = getReactApplicationContext().getSharedPreferences("my_prefs", Context.MODE_PRIVATE);SharedPreferences.Editor editor = sharedPref.edit();editor.putString(key, value);editor.apply();}@Overridepublic String getItem(String key) {SharedPreferences sharedPref = getReactApplicationContext().getSharedPreferences("my_prefs", Context.MODE_PRIVATE);return sharedPref.getString(key, null);}@Overridepublic void removeItem(String key) {SharedPreferences sharedPref = getReactApplicationContext().getSharedPreferences("my_prefs", Context.MODE_PRIVATE);sharedPref.edit().remove(key).apply();}@Overridepublic void clear() {SharedPreferences sharedPref = getReactApplicationContext().getSharedPreferences("my_prefs", Context.MODE_PRIVATE);sharedPref.edit().clear().apply();}
}
接下来,我们需要创建 NativeLocalStoragePackage,它将该模块注册到 React Native 运行时中,并将其包装为 Turbo Native Package:
android/app/src/main/java/com/nativelocalstorage/NativeLocalStoragePackage.java
package com.nativelocalstorage;import com.facebook.react.TurboReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.module.model.ReactModuleInfo;
import com.facebook.react.module.model.ReactModuleInfoProvider;import java.util.HashMap;
import java.util.Map;public class NativeLocalStoragePackage extends TurboReactPackage {@Overridepublic NativeModule getModule(String name, ReactApplicationContext reactContext) {if (name.equals(NativeLocalStorageModule.NAME)) {return new NativeLocalStorageModule(reactContext);} else {return null;}}@Overridepublic ReactModuleInfoProvider getReactModuleInfoProvider() {return new ReactModuleInfoProvider() {@Overridepublic Map<String, ReactModuleInfo> get() {Map<String, ReactModuleInfo> map = new HashMap<>();map.put(NativeLocalStorageModule.NAME, new ReactModuleInfo(NativeLocalStorageModule.NAME, // nameNativeLocalStorageModule.NAME, // classNamefalse, // canOverrideExistingModulefalse, // needsEagerInitfalse, // isCXXModuletrue // isTurboModule));return map;}};}
}
最后,我们需要在主应用中告诉 React Native 如何找到这个包,这称为在 React Native 中“注册”该包。
将其添加到 getPackages 方法中返回的列表中:
android/app/src/main/java/com/turbomoduleexample/MainApplication.java
package com.turbomoduleexample;import android.app.Application;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactHost;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactHost;
import com.facebook.react.defaults.DefaultReactNativeHost;
import com.facebook.soloader.SoLoader;
import com.nativelocalstorage.NativeLocalStoragePackage;import java.util.ArrayList;
import java.util.List;public class MainApplication extends Application implements ReactApplication {private final ReactNativeHost reactNativeHost = new DefaultReactNativeHost(this) {@Overridepublic List<ReactPackage> getPackages() {List<ReactPackage> packages = new PackageList(this).getPackages();packages.add(new NativeLocalStoragePackage());return packages;}@Overridepublic String getJSMainModuleName() {return "index";}@Overridepublic boolean getUseDeveloperSupport() {return BuildConfig.DEBUG;}@Overridepublic boolean isNewArchEnabled() {return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;}@Overridepublic boolean isHermesEnabled() {return BuildConfig.IS_HERMES_ENABLED;}};@Overridepublic ReactHost getReactHost() {return DefaultReactHost.getDefaultReactHost(getApplicationContext(), reactNativeHost);}@Overridepublic void onCreate() {super.onCreate();SoLoader.init(this, false);if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {DefaultNewArchitectureEntryPoint.load();}}
}
现在,你可以在模拟器上构建并运行代码:
yarn run android
5. 应用构建发布
在构建和发布 React Native 应用时,主要涉及到 iOS 和 Android 两个平台的特定配置和步骤。
以下是详细的内容,包括应用信息配置、图标配置、打包和发布的过程。
5.1. 应用信息配置
1. iOS 配置
- 项目设置:打开 Xcode,选择你的项目文件(.xcworkspace),在“General”选项卡中配置应用名称、版本、构建号和 Bundle Identifier。

- info.plist: 修改 Info.plist文件,添加应用的描述、权限说明等。
<key>CFBundleDisplayName</key>
<string>MyApp</string>
<key>CFBundleIdentifier</key>
<string>com.example.myapp</string>
<key>CFBundleVersion</key>
<string>1.0.0</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
- 启动页:配置 ios 项目下的 LaunchScreen。

2. Android 配置
- AndroidManifest.xml: 在 android/app/src/main/AndroidManifest.xml 中配置应用信息,如应用名称、图标、权限等。
<applicationandroid:name=".MainApplication"android:label="MyApp"android:icon="@mipmap/ic_launcher">...
</application>
- build.gradle: 在 android/app/build.gradle 文件中设置应用的版本和包名。
android {...defaultConfig {applicationId "com.example.myapp"versionCode 1versionName "1.0"}
}
5.2. 图标配置
图标的生成,可以使用 https://www.appicon.co/ 一键生成。
1. iOS 图标
- 图标资源:在 Xcode 的 Assets.xcassets 中,添加应用图标。确保按照 Apple 的图标规格提供不同尺寸的图标。

2. Android 图标
- 图标资源:在 android/app/src/main/res 目录下的各个 mipmap 文件夹中(如 mipmap-mdpi,mipmap-hdpi 等)放置不同尺寸的应用图标。
5.3. 打包
1. iOS 打包
-
使用 Xcode: 在 Xcode 中选择 Product > Archive 进行打包。
-
导出: 打包完成后,会打开 Archives 窗口,选择最新的构建,点击 "Distribute App",根据提示选择导出方式(App Store、Ad Hoc 等)。
2. Android 打包
- 命令行打包: 在项目根目录下,运行以下命令生成 APK。
cd android
./gradlew assembleRelease
- APK 文件: 打包完成后,APK 文件将位于 android/app/build/outputs/apk/release 目录中。
5.4. 发布
1. iOS 发布
-
App Store Connect: 登录 App Store Connect,创建一个新的应用条目,并填写必要的信息。
-
上传应用: 使用 Xcode 或者 Transporter 将生成的 .ipa 文件上传到 App Store Connect。
-
审核: 提交审核,等待 Apple 的审核通过。
2. Android 发布
-
Google Play Console: 登录 Google Play Console,创建一个新的应用。
-
上传 APK: 在“版本”部分上传生成的 APK 文件,填写应用的描述、图标、截图等信息。
-
发布: 提交应用,等待 Google 的审核通过。
6. 补充资料
-
React-Native 官方文档:https://reactnative.dev/docs/getting-started
-
React-Native 导航库:https://reactnavigation.org/docs/getting-started
-
React-Native 样式表:https://reactnative.dev/docs/stylesheet
-
Material Design UI 库:https://callstack.github.io/react-native-paper/
-
基于 Tailwind 的 UI 库:https://gluestack.io/
-
小清新 UI 库:https://tamagui.dev/
-
构建分发平台:https://www.pgyer.com/
-
苹果发布:https://appstoreconnect.apple.com/
-
老架构下的原生模块开发与集成:https://reactnative.dev/docs/legacy/native-modules-intro
-
新架构下的原生模块开发与集成:https://reactnative.dev/docs/turbo-native-modules-introduction
-
Turbo Native Module 架构:https://github.com/reactwg/react-native-new-architecture/blob/main/docs/turbo-modules.md
-
Fabric Native Components 架构:https://github.com/reactwg/react-native-new-architecture/blob/main/docs/fabric-native-components.md
-
React-Native 新架构:https://reactnative.dev/architecture/landing-page
-
优化 js 引擎:https://github.com/facebook/hermes
