mcpField
E
expo-best-practices

expo-best-practices

github.com/ofershap开发1143 次安装37 周前更新
代码仓库

关于 expo-best-practices

供处理 Expo 或 React Native 代码的 AI Agent 使用,归纳导航、原生配置、平台专属代码、存储和更新等开发任务的当前实践。

主要功能

  1. 01使用 Expo Router 实现文件路由
  2. 02通过配置插件设置原生项目
  3. 03使用 EAS Build 构建原生应用
  4. 04采用平台专属代码模式
  5. 05使用 Expo 包处理图片、字体和机密数据
  6. 06使用带类型的导航参数

适用场景

  1. 01组织路由和共享布局
  2. 02避免直接编辑文件来配置原生设置
  3. 03处理敏感数据与推送通知
  4. 04使用当前 API 维护 Expo 应用

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/ofershap/expo-best-practices --skill expo-best-practices

安装后在 Claude Code 中输入/expo-best-practices确认已加载。

详细介绍

适用场景

在处理 Expo 或 React Native 代码时使用此技能。它介绍当前的最佳实践,并避免 AI Agent 因使用过时模式、手动进行原生配置和调用已弃用 API 而犯下的常见错误。

关键规则

1. 使用 Expo Router 进行导航

错误(Agent 常见做法):

import { createStackNavigator } from "@react-navigation/stack";
const Stack = createStackNavigator();
export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="Home" component={HomeScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

正确:

app/
  _layout.tsx
  index.tsx
  settings.tsx

使用 app/ 目录约定。文件会自动成为路由。新的 Expo 项目默认包含 Expo Router。

原因: Expo Router 基于 React Navigation 构建,提供基于文件的路由、类型化路由、深度链接,并与 Expo CLI 集成。手动设置 React Navigation 不仅多余,还会错过 Expo Router 的功能。

2. 使用 expo-router 布局定义导航结构

错误:

// Each screen manually defines its own layout

正确:

// app/_layout.tsx
import { Stack } from "expo-router";
export default function RootLayout() {
  return <Stack />;
}

原因: _layout.tsx 定义共享导航结构、标签页、堆栈和嵌套布局。它能集中管理导航配置。

3. 使用 EAS Build 构建原生应用

错误:

expo build:ios
expo build:android

正确:

eas build --platform ios
eas build --platform android

原因: expo build 已弃用。EAS Build 是当前的云构建服务,提供更好的缓存、凭据管理和原生模块支持。

4. 使用 Expo 配置插件进行原生配置

错误:

# Manually editing
ios/MyApp/Info.plist
android/app/src/main/AndroidManifest.xml

正确:

// app.config.ts
import { ConfigPlugin } from "expo/config-plugins";
const withMyApiKey: ConfigPlugin<{ apiKey: string }> = (config, { apiKey }) => {
  config = withInfoPlist(config, (c) => {
    c.modResults["MY_API_KEY"] = apiKey;
    return c;
  });
  return config;
};
export default {
  plugins: [["withMyApiKey", { apiKey: process.env.API_KEY }]],
};

原因: 直接编辑原生文件会在预构建时被覆盖。配置插件会在构建时修改原生文件,并且这些修改在预构建后仍会保留。

5. 平台专属代码:使用平台扩展名或 Platform.select

错误:

if (Platform.OS === "ios") {
  return <IOSComponent />;
}
return <AndroidComponent />;

正确:

MyComponent.ios.tsx
MyComponent.android.tsx

或者:

import { Platform } from "react-native";
const styles = StyleSheet.create({
  container: Platform.select({
    ios: { paddingTop: 20 },
    android: { paddingTop: 0 },
  }),
});

原因: 平台文件扩展名让打包器只为相应平台包含正确的代码。在适当的情况下,Platform.select 可将条件逻辑保留在同一文件中。

6. 使用 expo-image 替代 React Native Image

错误:

import { Image } from "react-native";
<Image source={{ uri: url }} style={styles.image} />;

正确:

import { Image } from "expo-image";
<Image source={url} contentFit="cover" style={styles.image} />;

原因: expo-image 提供缓存、占位图、blurhash 和更好的性能。React Native 的 Image 不具备这些功能。

7. 使用 expo-font 加载自定义字体

错误:

# Manual font linking via native projects

正确:

import * as Font from "expo-font";
await Font.loadAsync({
  CustomFont: require("./assets/fonts/CustomFont.ttf"),
});

原因: expo-font 负责处理字体加载,避免手动配置原生端。

8. 使用 expo-secure-store 存储敏感数据

错误:

import AsyncStorage from "@react-native-async-storage/async-storage";
await AsyncStorage.setItem("authToken", token);

正确:

import * as SecureStore from "expo-secure-store";
await SecureStore.setItemAsync("authToken", token);

原因: AsyncStorage 不会加密。expo-secure-store 使用平台的 keychain/Keystore 存储令牌和机密信息。

9. 使用 expo-notifications 发送推送通知

错误:

// Manual Firebase/APNs setup, native configuration

正确:

import * as Notifications from "expo-notifications";
const token = await Notifications.getExpoPushTokenAsync();

原因: expo-notifications 抽象了 iOS 和 Android 上的推送设置流程。手动配置 Firebase/APNs 容易出错,而且因平台而异。

10. 使用 app.json 或 app.config.ts 进行配置

错误:

# Editing Info.plist or AndroidManifest.xml directly

正确:

{
  "expo": {
    "name": "MyApp",
    "slug": "my-app",
    "plugins": ["expo-router"]
  }
}

原因: Expo 根据 app.json/app.config.ts 管理原生配置。直接编辑原生文件会在预构建时被覆盖。

11. 使用 expo-updates 进行 OTA 更新

错误:

import codePush from "react-native-code-push";
export default codePush(MyApp);

正确:

import * as Updates from "expo-updates";
await Updates.checkForUpdateAsync();

原因: expo-updates 与 EAS 和 Expo 的更新服务集成。CodePush 需要单独设置,在 Expo 项目中属于多余方案。

12. 使用 TypeScript 和类型化导航参数

错误:

router.push("/profile");

正确:

import { router } from "expo-router";
router.push({ pathname: "/profile/[id]", params: { id: userId } });

在路由文件中定义路由参数。与 TypeScript 配合使用时,Expo Router 会提供类型化路由辅助工具。

原因: 类型化参数可避免运行时错误,并改善编辑器支持。

模式

  • 在 app/ 下创建路由,使用映射到 URL 的文件名
  • 使用 _layout.tsx 定义共享布局、标签页和堆栈
  • 将平台专属组件放在 Component.ios.tsx 和 Component.android.tsx 中
  • 任何原生配置变更都使用 app.json / app.config.ts 中的 expo config plugins
  • 使用 eas.json 配置 EAS Build 配置文件

反模式

  • 不要在 Expo 项目中手动设置 React Navigation
  • 不要使用 expo build(请使用 EAS Build)
  • 在托管工作流中,不要直接编辑 ios/ 或 android/
  • 不要使用 AsyncStorage 存储身份验证令牌或机密信息
  • expo-image 可用时,不要使用 React Native 的 Image
  • 不要在 Expo 项目中使用 CodePush(请使用 expo-updates)

评论

支持 Markdown