@gaozh1024/expo-starter
v0.2.13
Published
Expo starter template based on @gaozh1024/rn-kit
Maintainers
Readme
Expo Starter
基于 @gaozh1024/rn-kit 的 React Native 项目模板。
通过 create-expo-app 创建项目
推荐使用带 scope 的包名发布模板,避免 expo-starter 这类通用包名和 npm 上已有包冲突:
npx create-expo-app@latest my-app --template @gaozh1024/expo-starter如果你发布后仍使用
--template expo-starter,create-expo-app 解析到的可能不是你的模板包。
特性
- ✅ Provider 初始化顺序固定,开箱可跑
- ✅ 目录层级稳定,职责清晰
- ✅ 首版页面覆盖基础产品能力
- ✅ 结构不过度设计,后续可自然扩展
- ✅ 内置 11 个基础页面
- ✅ Mock 数据支持,无需后端即可开发
- ✅ 内置 Logo 图片资源与应用图标配置
- ✅ 已内置
expo-image,可直接使用框架AppImage - ✅ 已内置
expo-linear-gradient,可直接使用GradientView - ✅ 已内置
@expo/vector-icons,可稳定使用框架Icon - ✅ 基于 Expo SDK 54 / React Native 0.81 依赖基线维护
- ✅ 表单页已接入键盘收起交互,输入体验更一致
- ✅ 开发环境已预配置日志浮层、错误边界与 API 自动打点
- ✅ API 示例默认带敏感字段脱敏,便于发布前直接联调
- ✅ 已预置统一骨架屏能力,可直接使用
Skeleton/SkeletonText/SkeletonAvatar - ✅ 设置页已接入框架
Switch,登录/注册/找回密码页使用框架AppButton - ✅ 页面容器优先使用
AppScreen surface="background"自动适配主题
页面列表
| 页面 | 路径 | 说明 |
| -------------- | ------------------------------------------------ | -------------------------- |
| Launch | features/launch/screens/LaunchScreen.tsx | 启动页,自动跳转登录或首页 |
| Login | features/auth/screens/LoginScreen.tsx | 登录页 |
| Register | features/auth/screens/RegisterScreen.tsx | 注册页 |
| ForgotPassword | features/auth/screens/ForgotPasswordScreen.tsx | 找回密码 |
| Home | features/home/screens/HomeScreen.tsx | 首页 |
| My | features/profile/screens/MyScreen.tsx | 我的页面 |
| UserInfo | features/profile/screens/UserInfoScreen.tsx | 用户信息 |
| Settings | features/profile/screens/SettingsScreen.tsx | 设置中心 |
| Theme | features/profile/screens/ThemeScreen.tsx | 主题切换 |
| Language | features/profile/screens/LanguageScreen.tsx | 语言切换 |
| About | features/profile/screens/AboutScreen.tsx | 关于我们 |
目录结构
expo-starter/
├── index.js # Expo 注册入口
├── App.tsx # 应用根组件
├── src/
│ ├── root/ # 应用级装配代码
│ │ ├── RootApp.tsx # 根应用,启动流程控制
│ │ └── providers.tsx # 统一 Provider 配置
│ ├── bootstrap/ # 启动配置层
│ │ ├── app-config.ts # 应用配置
│ │ ├── theme.ts # 主题配置
│ │ └── constants.ts # 常量定义
│ ├── navigation/ # 导航定义
│ │ ├── RootNavigator.tsx # 根导航器(单层导航)
│ │ ├── MainTabs.tsx # 主 Tab 导航
│ │ ├── routes.ts # 路由常量
│ │ └── types.ts # 导航类型
│ ├── features/ # 业务域
│ │ ├── launch/ # 启动
│ │ ├── auth/ # 认证
│ │ ├── home/ # 首页
│ │ └── profile/ # 个人中心
│ ├── components/ # 公共组件
│ │ └── common/ # 通用组件
│ ├── data/ # 数据层
│ │ ├── api.ts # API 工厂
│ │ ├── schemas.ts # 数据 Schema
│ │ ├── session.ts # Session 管理
│ │ └── mocks/ # Mock 数据
│ ├── store/ # 全局状态
│ │ ├── session.store.ts # 登录态
│ │ └── ui.store.ts # UI 状态
│ ├── utils/ # 工具函数
│ └── types/ # 类型定义
├── global.css # 全局样式
├── tailwind.config.js # Tailwind 配置
├── babel.config.js # Babel 配置
├── metro.config.js # Metro 配置
└── package.json当前模板采用单层根导航结构:所有页面统一注册在
RootNavigator中,MainTabs仅包含 Home 与 My 两个 Tab,二级页面(如 Settings、UserInfo 等)与 Tab 同级挂在 RootStack 下。这种结构在 rn-kit 0.4.x 推荐用法下更简洁,也便于全局手势与页面转场统一管理。
快速开始
安装依赖
npm install
# 或
yarn install
# 或
pnpm install启动开发服务器
npx expo start持久化 storage 接入示例(推荐)
模板里 session.ts、框架 logger 按钮位置等能力都会使用 @gaozh1024/rn-kit 导出的 storage。
框架默认是内存 storage。 如果你希望登录态、用户信息、日志按钮位置在应用重启后继续保留,推荐在项目启动时注入持久化实现:
1. 安装 AsyncStorage
npx expo install @react-native-async-storage/async-storage2. 新增启动文件
// src/bootstrap/storage.ts
import AsyncStorage from '@react-native-async-storage/async-storage';
import { setStorageAdapter } from '@gaozh1024/rn-kit';
setStorageAdapter({
getItem: key => AsyncStorage.getItem(key),
setItem: (key, value) => AsyncStorage.setItem(key, value),
removeItem: key => AsyncStorage.removeItem(key),
});3. 在应用入口最早执行
// App.tsx
import 'react-native-gesture-handler';
import 'react-native-reanimated';
import './global.css';
import './src/bootstrap/storage';这样模板里的:
src/data/session.tscreateAPI(...getHeaders)- logger 按钮位置持久化
都会自动走你注入的持久化 storage。
依赖兼容性说明
当前模板以 Expo SDK 54.0.x + React Native 0.81.x 为基线维护,配套 @gaozh1024/rn-kit ^0.4.15。
如果你准备把 @gaozh1024/rn-kit 接入到其他 Expo 项目,建议:
- 先确认你的 Expo SDK 版本
- 使用
npx expo install ...安装原生依赖 - 再安装
@gaozh1024/rn-kit
尤其是:
expo-imagereact-native-reanimatedreact-native-workletsreact-native-gesture-handlerreact-native-safe-area-contextreact-native-screens
不要直接让 npm 在 Expo 项目里自由解析这些依赖的最新版本。
补充说明:
- 当前模板已经默认安装
expo-image,新建项目后可直接使用AppImage - 只有把
rn-kit >= 0.4.6接入到旧项目时,才需要手动执行npx expo install expo-image
本地测试模板
推荐在模板发布前,先走一遍本地安装链路验证。
1. 用本地模板创建项目
npx create-expo-app template-app --template file:/absolute/path/to/templates/expo-starter例如:
npx create-expo-app template-app --template file:/Users/gzh/Projects/framework/rn-monorepo/templates/expo-starter2. 注入本地 rn-kit
如果你正在联调本地框架,可以在新项目中执行:
yalc add @gaozh1024/rn-kit
npm install然后启动:
yarn start
# 或
npx expo start这个流程适合验证:
- 模板能否被
create-expo-app正常消费 - 本地
rn-kit改动能否正确注入模板项目 - 页面、导航、状态栏、底部 Tab 是否都正常工作
- 渐变背景页面是否正常显示
发布前检查
- 先发布
@gaozh1024/rn-kit - 再发布当前模板包
- 确认
app.json中的icon / splash / adaptiveIcon / favicon对应资源都已打包 - 确认模板里的
expo版本和你希望支持的 Expo Go SDK 一致 - 确认 README 中的模板包名、scope 包名、安装命令与实际发布信息一致
- 确认
app.json里的name / slug / scheme / ios.bundleIdentifier / android.package是否为你希望交付给模板使用者的默认值
运行到设备
# iOS
npx expo start --ios
# Android
npx expo start --android开发指南
页面实现约定
模板页面默认优先使用 rn-kit 提供的组件与能力:
- 布局:
AppScreen/SafeScreen/AppView/Center/AppScrollView - 展示:
AppText/Card/Icon/AppImage - 交互:
AppButton/AppPressable - 表单:
AppInput/Select/Picker/DatePicker/Switch - 反馈:
Toast/Loading/Skeleton/SkeletonText/SkeletonAvatar/useAlert - 状态栏:
AppStatusBar/AppFocusedStatusBar - 导航:
StackNavigator/TabNavigator/useNavigation - 可观测性:
useLogger/createAPI(...observability)
页面层尽量不要直接从 react-native 或 @react-navigation/* 引入基础 UI / 导航能力,优先使用 rn-kit 暴露的统一 API。
AppPressable已支持基础容器快捷参数,适合直接写可点击卡片、列表行、筛选项AppText也支持文本场景常用的快捷参数(如p/m/w/rounded/bg),但不建议把它当布局容器使用GradientView/FormItem也已支持基础容器快捷参数AppButton/AppInput/Select/Picker/DatePicker/Switch已支持常用外层快捷参数,适合直接控制mt/w/h/roundedCheckbox/Radio/Slider/CheckboxGroup/RadioGroup/Progress也已补齐常用基础快捷参数,表单与状态组件现在可以直接做快速排版AppList/AppImage/Icon也已支持基础快捷参数,列表、图片、图标入口的快速排版会更统一
页面容器推荐这样区分:
- 常规业务页(尤其是带
AppHeader的页面):优先使用AppScreen surface="background" - 无 Header 的全屏页、沉浸式背景页:使用
SafeScreen - 如果页面没有
AppHeader但仍需要顶部安全区,请显式使用AppScreen top
带 AppHeader 的页面通常不需要单独处理状态栏,推荐直接:
<AppScreen surface="background">
<AppHeader title="设置" />
<AppScrollView>{/* 页面内容 */}</AppScrollView>
</AppScreen>此时:
AppHeader会自动注入聚焦态透明状态栏AppScreen默认top=false,顶部安全区由AppHeader自己承接status bar + header会保持同一块背景,避免顶部颜色割裂- 底部安全区仍由
AppScreen承接
如果页面没有 Header,推荐这样写:
<AppScreen top surface="background">
<AppView flex>{/* 页面内容 */}</AppView>
</AppScreen>例如登录页、启动页这类全屏彩色背景页面,推荐在页面内局部覆盖状态栏:
<>
<AppFocusedStatusBar barStyle="light-content" translucent backgroundColor="transparent" />
<GradientView colors={['#f38b32', '#fb923c']} style={{ flex: 1 }}>
<SafeScreen flex top={false}>
{/* 页面内容 */}
</SafeScreen>
</GradientView>
</>模板已经预装 expo-linear-gradient,因此可以直接使用框架导出的 GradientView。
输入框尺寸如果需要统一调整,推荐直接:
<AppInput placeholder="请输入手机号" style={{ height: 56 }} inputStyle={{ fontSize: 16 }} />其中 style.height 会作用到输入框外层容器,适合整体高度控制。
图片与骨架屏约定
模板已经预装 expo-image,因此可以直接使用框架导出的 AppImage:
<AppImage
source={{ uri: user.avatar }}
width={72}
height={72}
borderRadius="full"
cachePolicy="memory-disk"
/>加载中的卡片、资料区、列表占位,推荐直接使用统一骨架组件:
<AppView p={4} gap={3}>
<SkeletonAvatar size={48} />
<SkeletonText lines={3} lineWidths={['100%', '88%', '60%']} />
</AppView>如果只是通用列表加载,优先使用框架的 AppList loading,内部已经接入统一骨架样式。
开发态可观测性
模板默认通过 AppProvider 承接框架的开发态可观测性能力。
开发环境下建议直接使用:
useLogger(namespace)记录页面和交互日志createAPI({ observability })自动记录请求、响应和错误enableErrorBoundary捕获 React 渲染错误
示例:
import { AppButton, useLogger } from '@gaozh1024/rn-kit';
export function LoginAction() {
const logger = useLogger('auth');
return (
<AppButton
onPress={() => {
logger.info('点击登录按钮');
}}
>
登录
</AppButton>
);
}如果你想显式控制,可以在 src/root/providers.tsx 中给 AppProvider 传:
<AppProvider
enableLogger
enableErrorBoundary
loggerProps={{
level: 'info',
overlayEnabled: true,
exportEnabled: true,
}}
errorBoundaryProps={{
title: '应用渲染异常',
description: '开发环境已启用错误边界,可结合日志浮层快速排查问题。',
showDetails: true,
resetText: '重新加载',
}}
>
<RootApp />
</AppProvider>框架会在开发环境下提供:
- CLI 彩色日志输出
- App 内
LogOverlay - level / namespace / 搜索 / 导出
- API
request / response / error自动打点
详细说明见:
packages/rn-kit/README.mddocs/04-开发规范/开发态可观测性说明.md
键盘交互约定
模板中的表单页推荐统一遵循以下约定:
- 常规页面容器优先使用
AppScreen dismissKeyboardOnPressOutside - 全屏无 Header 页面优先使用
SafeScreen dismissKeyboardOnPressOutside - 滚动容器优先使用
AppScrollView dismissKeyboardOnPressOutside - 提交按钮默认保留
AppButton的dismissKeyboardOnPress={true}
这样用户点击空白区域或提交按钮时,键盘会自动收起,减少焦点残留与遮挡问题。
局部表单块如果不是整页滚动容器,也可以用:
import { KeyboardDismissView } from '@gaozh1024/rn-kit';
<KeyboardDismissView>{/* 局部输入区域 */}</KeyboardDismissView>;选择器与弹层约定
当前模板里与选择器相关的 UI 约定已经和框架保持一致:
Select:适合单选 / 多选项列表Picker:适合通用多列选择DatePicker:当前本质上是基于多列滚轮的日期选择器
这三者的底部弹层样式与动画已统一:
- 遮罩层淡入淡出
- 面板从底部进入 / 退出
- 支持点击遮罩关闭
如果业务后续需要省市区、级联项等滚轮选择,优先基于 Picker 扩展。
主题切换说明
模板已改为通过 AppProvider isDark 受控切换主题,切换浅色 / 深色 / 跟随系统时不再依赖重新挂载根组件。
页面实现上优先使用这些主题语义能力:
- 容器背景:
surface="background"/surface="card" - 文本颜色:
tone="default"/tone="muted"/color="primary-500" - 页面容器:
AppScreen/SafeScreen/AppView/AppScrollView
如果页面里直接写死 bg-white、text-gray-700 这类类名,暗色模式下通常就不会自动跟随主题。
底部导航栏
模板中的 MainTabs 默认使用 rn-kit 内置的 BottomTabBar,通过 tabBarOptions 统一配置高度、颜色和样式:
<TabNavigator
tabBarOptions={{
activeTintColor: '#f38b32',
inactiveTintColor: '#9ca3af',
activeBackgroundColor: '#fff7ed',
height: 72,
style: { borderTopWidth: 0, backgroundColor: '#ffffff' },
}}
>
{/* screens */}
</TabNavigator>导航类型约定
模板导航相关代码集中在:
src/navigation/types.ts:参数列表与导航类型别名src/navigation/routes.ts:路由名称常量src/navigation/*.tsx:基于rn-kit的导航器装配
推荐页面内这样写:
import { useNavigation } from '@gaozh1024/rn-kit';
import type { RootNavigationProp } from '@/navigation/types';
export function LoginScreen() {
const navigation = useNavigation<RootNavigationProp>();
return null;
}这样页面只依赖:
rn-kit提供的导航 hook- 模板自己的导航类型文件
不会直接耦合 @react-navigation/native 或 @react-navigation/stack。
导航结构说明:
RootNavigator:根导航器,管理整个应用的路由栈MainTabs:主 Tab 导航,包含 Home 和 My 两个标签页- 认证页面(Login、Register、ForgotPassword)和二级页面(Theme、Language、UserInfo、Settings、About)都在 RootStack 中注册
添加新页面
- 在
src/features/{domain}/screens/创建页面组件 - 在
src/navigation/types.ts添加路由参数类型 - 如有需要,在
src/navigation/types.ts增加页面对应的导航类型别名 - 在
RootNavigator.tsx中注册页面
添加新 API
- 在对应 feature 的
api.ts中定义端点 - 在
src/data/schemas.ts中定义输入输出 Schema - 在
src/data/mocks/中添加 Mock 数据(如需要)
组件放置规则
- 页面私有组件:放在页面文件附近
- 业务域内复用组件:放在
features/{domain}/components/ - 跨业务域复用组件:放在
components/common/
技术栈
许可证
MIT
