npx skills add https://github.com/allen-hsu/arsenal --skill react-native-mobile-dev使用 React Native 和 Expo 构建生产就绪移动应用程序的专业指导,遵循现代最佳实践。
使用 create-expo-stack (rn.new) 根据您的首选配置来搭建新的 React Native 应用。
交互式 CLI,让您选择技术栈:
# 最简单的方式
npx rn-new@latest
# 或使用包管理器
pnpm create expo-stack
npm create expo-stack
yarn create expo-stack
bun create expo-stack
CLI 将提示您选择:
用于匹配我们技术栈的生产就绪设置:
# 包含 Expo Router + NativeWind + i18n 的完整生产设置
pnpm create expo-stack MyApp --expo-router --tabs --nativewind --i18next --import-alias
cd MyApp
pnpm ios # iOS 模拟器
pnpm android # Android 模拟器
| 类别 | 选项 |
|---|---|
Expert guidance for building production-ready mobile applications using React Native and Expo with modern best practices.
Use create-expo-stack (rn.new) to scaffold new React Native apps with your preferred configuration.
Interactive CLI that lets you choose your tech stack:
# Simplest way
npx rn-new@latest
# Or with package managers
pnpm create expo-stack
npm create expo-stack
yarn create expo-stack
bun create expo-stack
The CLI will prompt you to select:
For production-ready setup matching our tech stack:
# Full production setup with Expo Router + NativeWind + i18n
pnpm create expo-stack MyApp --expo-router --tabs --nativewind --i18next --import-alias
cd MyApp
pnpm ios # iOS simulator
pnpm android # Android emulator
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
--npm, --yarn, --pnpm, --bun |
| 导航 | --expo-router, --react-navigation |
| 导航类型 | --tabs, --drawer (默认: stack) |
| 样式 | --nativewind, --unistyles, --tamagui, --restyle, --stylesheet |
| 后端 | --firebase, --supabase |
| 功能 | --i18next, --import-alias |
| 其他 | -d/--default, --no-git, --no-install |
npm install -g pnpm)app.config.ts 中更新您的应用名称、包标识符等.env 中配置环境变量eas init 以设置 EAS Build基于 create-expo-stack (Expo SDK 54):
| 类别 | 技术 |
|---|---|
| 框架 | React Native 0.81 + React 19 + Expo SDK 54 |
| 导航 | Expo Router 6 (基于文件的路由,支持类型化路由) |
| 样式 | NativeWind 4 (Tailwind CSS) 或 Unistyles 3 |
| 状态管理 | Zustand (需手动添加) |
| 数据获取 | TanStack Query (React Query) + Axios (需手动添加) |
| 表单 | React Hook Form + Zod (需手动添加) |
| 存储 | MMKV (推荐) 或 AsyncStorage |
| 国际化 | i18next + react-i18next (使用 --i18next 标志) |
| 离线支持 | NetInfo + 自定义队列 (需手动添加) |
| 身份验证 | Supabase 或 Firebase (可选标志) |
| 测试 | Jest + React Native Testing Library |
遵循 references/project-structure.md 中的推荐结构。
├── app/ # Expo Router 屏幕和布局
├── src/
│ ├── core/ # 核心基础设施 (基础层)
│ │ ├── config/ # 环境变量,常量
│ │ ├── services/ # API 客户端,存储,网络,日志
│ │ │ ├── api/ # HTTP 客户端,拦截器,错误处理器
│ │ │ ├── network/ # NetInfo 监视器,离线队列,同步
│ │ │ └── storage/ # MMKV 包装器
│ │ ├── i18n/ # i18next 配置,RTL 支持
│ │ └── providers/ # 主题,Query,i18n,网络提供者
│ ├── features/ # 功能模块 (自包含)
│ │ └── [feature]/ # screens/, widgets/, api/, hooks/, store/
│ ├── shared/ # 共享组件,钩子,工具
│ │ ├── components/ # ui/, forms/, layout/, feedback/
│ │ └── hooks/ # useDebounce, useMounted 等
│ ├── store/ # 全局状态 (auth, theme, network)
│ └── __tests__/ # 测试设置,模拟,工具
├── assets/ # 静态资源 (图片,字体)
└── locales/ # i18n JSON 文件 (common, errors, features)
app/ → features/, shared/, core/, store/
features/ → shared/, core/, store/ (禁止跨功能模块导入)
shared/ → core/ only
core/ → 无依赖 (基础层)
store/ → core/, shared/
详细约定请参考 references/coding-standards.md。
kebab-case (例如 login-form.tsx)@/ 别名进行绝对导入 (使用 --import-alias 配置)import type使用带有 NativeWind 的 Tailwind CSS 类。对于复杂的变体,使用 tailwind-variants:
import { tv } from "tailwind-variants";
import { Pressable, Text } from "react-native";
const button = tv({
base: "rounded-xl px-4 py-3 items-center justify-center",
variants: {
variant: {
primary: "bg-primary-500",
secondary: "bg-neutral-200 dark:bg-neutral-700",
destructive: "bg-danger-500",
},
disabled: {
true: "opacity-50",
},
},
defaultVariants: {
variant: "primary",
},
});
type ButtonProps = {
label: string;
variant?: "primary" | "secondary" | "destructive";
disabled?: boolean;
onPress: () => void;
};
export const Button = ({ label, variant, disabled, onPress }: ButtonProps) => (
<Pressable
className={button({ variant, disabled })}
onPress={onPress}
disabled={disabled}
>
<Text className="text-white font-semibold">{label}</Text>
</Pressable>
);
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import * as z from "zod";
const schema = z.object({
email: z.string().email("Invalid email"),
password: z.string().min(6, "Min 6 characters"),
});
type FormData = z.infer<typeof schema>;
export const LoginForm = ({
onSubmit,
}: {
onSubmit: (data: FormData) => void;
}) => {
const { control, handleSubmit } = useForm<FormData>({
resolver: zodResolver(schema),
});
return (
<View>
<ControlledInput control={control} name="email" label="Email" />
<ControlledInput
control={control}
name="password"
label="Password"
secureTextEntry
/>
<Button label="Login" onPress={handleSubmit(onSubmit)} />
</View>
);
};
import { create } from "zustand";
import { createSelectors } from "@/lib/utils";
interface AuthState {
token: string | null;
status: "idle" | "signIn" | "signOut";
signIn: (token: string) => void;
signOut: () => void;
}
const _useAuth = create<AuthState>((set) => ({
token: null,
status: "idle",
signIn: (token) => set({ token, status: "signIn" }),
signOut: () => set({ token: null, status: "signOut" }),
}));
// 使用 createSelectors 进行优化的重新渲染
export const useAuth = createSelectors(_useAuth);
// 导出操作以供在 React 外部使用
export const signIn = (token: string) => _useAuth.getState().signIn(token);
export const signOut = () => _useAuth.getState().signOut();
// api/posts/use-posts.ts
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { client } from "../common/client";
import type { Post } from "./types";
export const usePosts = () => {
return useQuery<Post[]>({
queryKey: ["posts"],
queryFn: () => client.get("/posts").then((res) => res.data),
});
};
export const useCreatePost = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: Omit<Post, "id">) => client.post("/posts", data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["posts"] });
},
});
};
app/
├── _layout.tsx # 根布局
├── index.tsx # 主屏幕 (/)
├── login.tsx # 登录屏幕 (/login)
├── (tabs)/ # 标签页组
│ ├── _layout.tsx # 标签页布局
│ ├── home.tsx # /home 标签页
│ └── profile.tsx # /profile 标签页
└── [id].tsx # 动态路由 (/:id)
// app/_layout.tsx
import { useAuth } from "@/lib/auth";
import { Redirect, Stack } from "expo-router";
export default function RootLayout() {
const status = useAuth.use.status();
if (status === "signOut") {
return <Redirect href="/login" />;
}
return <Stack />;
}
在 env.js 中使用 Zod 进行验证:
const client = z.object({
APP_ENV: z.enum(["development", "staging", "production"]),
API_URL: z.string().url(),
VERSION: z.string(),
});
// 通过 @env 别名访问
import Env from "@env";
console.log(Env.API_URL);
import { render, screen, fireEvent } from "@/lib/test-utils";
import { LoginForm } from "./login-form";
describe("LoginForm", () => {
it("validates email format", async () => {
const onSubmit = jest.fn();
render(<LoginForm onSubmit={onSubmit} />);
fireEvent.changeText(screen.getByTestId("email-input"), "invalid");
fireEvent.press(screen.getByTestId("login-button"));
expect(await screen.findByText("Invalid email")).toBeTruthy();
expect(onSubmit).not.toHaveBeenCalled();
});
});
# 开发
pnpm start # 启动 Expo 开发服务器
pnpm ios # 在 iOS 模拟器上运行
pnpm android # 在 Android 模拟器上运行
# 测试
pnpm test # 运行测试
pnpm lint # 运行 ESLint
pnpm typecheck # 运行 TypeScript 检查
# 构建
APP_ENV=staging pnpm build:ios # 构建 iOS (staging)
APP_ENV=production pnpm build:android # 构建 Android (production)
每周安装数
1
仓库
首次出现
1 天前
安全审计
安装于
amp1
cline1
opencode1
cursor1
kimi-cli1
codex1
| Category | Options |
|---|---|
| Package Manager | --npm, --yarn, --pnpm, --bun |
| Navigation | --expo-router, --react-navigation |
| Nav Type | --tabs, --drawer (default: stack) |
| Styling | --nativewind, --unistyles, --tamagui, --restyle, --stylesheet |
| Backend | --firebase, --supabase |
| Features | --i18next, --import-alias |
| Other | -d/--default, --no-git, --no-install |
npm install -g pnpm)app.config.ts with your app name, bundle ID, etc..enveas init to set up EAS BuildDocs: https://docs.rn.new/ | GitHub: https://github.com/roninoss/create-expo-stack
Based on create-expo-stack (Expo SDK 54):
| Category | Technology |
|---|---|
| Framework | React Native 0.81 + React 19 + Expo SDK 54 |
| Navigation | Expo Router 6 (file-based routing with typed routes) |
| Styling | NativeWind 4 (Tailwind CSS) or Unistyles 3 |
| State Management | Zustand (add manually) |
| Data Fetching | TanStack Query (React Query) + Axios (add manually) |
| Forms | React Hook Form + Zod (add manually) |
| Storage | MMKV (recommended) or AsyncStorage |
| i18n | i18next + react-i18next (use --i18next flag) |
| Offline Support | NetInfo + Custom Queue (add manually) |
| Authentication | Supabase or Firebase (optional flags) |
| Testing | Jest + React Native Testing Library |
Follow the recommended structure from references/project-structure.md.
├── app/ # Expo Router screens and layouts
├── src/
│ ├── core/ # Core infrastructure (base layer)
│ │ ├── config/ # Environment variables, constants
│ │ ├── services/ # API client, storage, network, logger
│ │ │ ├── api/ # HTTP client, interceptors, error handler
│ │ │ ├── network/ # NetInfo monitor, offline queue, sync
│ │ │ └── storage/ # MMKV wrapper
│ │ ├── i18n/ # i18next config, RTL support
│ │ └── providers/ # Theme, Query, i18n, Network providers
│ ├── features/ # Feature modules (self-contained)
│ │ └── [feature]/ # screens/, widgets/, api/, hooks/, store/
│ ├── shared/ # Shared components, hooks, utils
│ │ ├── components/ # ui/, forms/, layout/, feedback/
│ │ └── hooks/ # useDebounce, useMounted, etc.
│ ├── store/ # Global state (auth, theme, network)
│ └── __tests__/ # Test setup, mocks, utilities
├── assets/ # Static assets (images, fonts)
└── locales/ # i18n JSON files (common, errors, features)
app/ → features/, shared/, core/, store/
features/ → shared/, core/, store/ (NO cross-feature imports)
shared/ → core/ only
core/ → No dependencies (base layer)
store/ → core/, shared/
Refer to references/coding-standards.md for detailed conventions.
kebab-case for all files and folders (e.g., login-form.tsx)@/ alias for absolute imports (configure with --import-alias)import type for type-only importsUse Tailwind CSS classes with NativeWind. For complex variants, use tailwind-variants:
import { tv } from "tailwind-variants";
import { Pressable, Text } from "react-native";
const button = tv({
base: "rounded-xl px-4 py-3 items-center justify-center",
variants: {
variant: {
primary: "bg-primary-500",
secondary: "bg-neutral-200 dark:bg-neutral-700",
destructive: "bg-danger-500",
},
disabled: {
true: "opacity-50",
},
},
defaultVariants: {
variant: "primary",
},
});
type ButtonProps = {
label: string;
variant?: "primary" | "secondary" | "destructive";
disabled?: boolean;
onPress: () => void;
};
export const Button = ({ label, variant, disabled, onPress }: ButtonProps) => (
<Pressable
className={button({ variant, disabled })}
onPress={onPress}
disabled={disabled}
>
<Text className="text-white font-semibold">{label}</Text>
</Pressable>
);
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import * as z from "zod";
const schema = z.object({
email: z.string().email("Invalid email"),
password: z.string().min(6, "Min 6 characters"),
});
type FormData = z.infer<typeof schema>;
export const LoginForm = ({
onSubmit,
}: {
onSubmit: (data: FormData) => void;
}) => {
const { control, handleSubmit } = useForm<FormData>({
resolver: zodResolver(schema),
});
return (
<View>
<ControlledInput control={control} name="email" label="Email" />
<ControlledInput
control={control}
name="password"
label="Password"
secureTextEntry
/>
<Button label="Login" onPress={handleSubmit(onSubmit)} />
</View>
);
};
import { create } from "zustand";
import { createSelectors } from "@/lib/utils";
interface AuthState {
token: string | null;
status: "idle" | "signIn" | "signOut";
signIn: (token: string) => void;
signOut: () => void;
}
const _useAuth = create<AuthState>((set) => ({
token: null,
status: "idle",
signIn: (token) => set({ token, status: "signIn" }),
signOut: () => set({ token: null, status: "signOut" }),
}));
// Use createSelectors for optimized re-renders
export const useAuth = createSelectors(_useAuth);
// Export actions for use outside React
export const signIn = (token: string) => _useAuth.getState().signIn(token);
export const signOut = () => _useAuth.getState().signOut();
// api/posts/use-posts.ts
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { client } from "../common/client";
import type { Post } from "./types";
export const usePosts = () => {
return useQuery<Post[]>({
queryKey: ["posts"],
queryFn: () => client.get("/posts").then((res) => res.data),
});
};
export const useCreatePost = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: Omit<Post, "id">) => client.post("/posts", data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["posts"] });
},
});
};
app/
├── _layout.tsx # Root layout
├── index.tsx # Home screen (/)
├── login.tsx # Login screen (/login)
├── (tabs)/ # Tab group
│ ├── _layout.tsx # Tab layout
│ ├── home.tsx # /home tab
│ └── profile.tsx # /profile tab
└── [id].tsx # Dynamic route (/:id)
// app/_layout.tsx
import { useAuth } from "@/lib/auth";
import { Redirect, Stack } from "expo-router";
export default function RootLayout() {
const status = useAuth.use.status();
if (status === "signOut") {
return <Redirect href="/login" />;
}
return <Stack />;
}
Use Zod for validation in env.js:
const client = z.object({
APP_ENV: z.enum(["development", "staging", "production"]),
API_URL: z.string().url(),
VERSION: z.string(),
});
// Access via @env alias
import Env from "@env";
console.log(Env.API_URL);
import { render, screen, fireEvent } from "@/lib/test-utils";
import { LoginForm } from "./login-form";
describe("LoginForm", () => {
it("validates email format", async () => {
const onSubmit = jest.fn();
render(<LoginForm onSubmit={onSubmit} />);
fireEvent.changeText(screen.getByTestId("email-input"), "invalid");
fireEvent.press(screen.getByTestId("login-button"));
expect(await screen.findByText("Invalid email")).toBeTruthy();
expect(onSubmit).not.toHaveBeenCalled();
});
});
# Development
pnpm start # Start Expo dev server
pnpm ios # Run on iOS simulator
pnpm android # Run on Android emulator
# Testing
pnpm test # Run tests
pnpm lint # Run ESLint
pnpm typecheck # Run TypeScript check
# Building
APP_ENV=staging pnpm build:ios # Build iOS (staging)
APP_ENV=production pnpm build:android # Build Android (production)
Weekly Installs
1
Repository
First Seen
1 day ago
Security Audits
Installed on
amp1
cline1
opencode1
cursor1
kimi-cli1
codex1
Flutter应用架构设计指南:分层结构、数据层实现与最佳实践
4,000 周安装
软件架构解决方案技能:PRD文档生成、需求分析与系统设计指南
334 周安装
React + Cloudflare Workers 集成 Microsoft Entra ID 身份验证完整指南 | Azure Auth
327 周安装
Neon Postgres 连接池与无服务器驱动配置指南:Prisma、Drizzle 集成教程
1 周安装
用户故事生成器:遵循3C和INVEST标准,快速创建高质量用户故事模板
342 周安装
Cloudflare Images 图像托管与转换 API 使用指南 | 支持 AI 人脸裁剪与内容凭证
328 周安装
Cloudflare MCP Server 教程:在Cloudflare Workers上构建远程模型上下文协议服务器
328 周安装