应用门禁
自定义 onboarding、协议同意、版本、生物识别、认证和评分流程。
Expo 模板的启动门禁是围绕 router <Stack> 的兄弟组件,不是 native wrapper
chain。自定义时要区分哪些 gate 会 redirect,哪些 gate 会覆盖当前页面。
真实拓扑
app/_layout.tsx 先挂载 providers,再按这个顺序渲染 gates:
RootErrorBoundary
-> providers
-> OnboardingGate
-> AuthGate
-> VersionGate
-> <Stack />
-> ConsentGate
-> BiometricGate
-> PortalHost| Gate | 行为 | 说明 |
|---|---|---|
RootErrorBoundary | Error boundary | 包住整个 app,并上报到 Sentry |
OnboardingGate | Redirect | 首次启动用户会去 /onboarding,但 onboarding、refer、legal 路由除外 |
AuthGate | Redirect cleanup | guest-first;只在登录成功后关闭 auth 页面 |
VersionGate | Overlay modal/dialog | 强制更新会阻断;软更新可关闭 |
ConsentGate | Overlay modal | 要求接受当前 legal version;阅读 legal 页面时不弹出 |
BiometricGate | 全屏 overlay | 对启用生物识别锁的已登录用户锁住全局页面 |
Onboarding
lib/onboarding.tsx 把完成状态存在自己的 cache key:
growth.onboarding.v1app/onboarding.tsx 渲染四张 slide:
launch -> protect -> grow -> permissions完成 onboarding 会发送 onboarding completed analytics event、记录一次评分触发、持久化 { completed: true, completedAt },然后替换路由到 Home。
Auth gate
应用是 guest-first。AuthGate 不会在启动时把整个 app 锁在登录页。需要登录的动作由具体屏幕或组件里的 SignInGate 处理。
当用户已经登录,但仍停留在 (auth) 组且不是 password recovery 时,AuthGate 会关闭认证流程:
canGoBack() -> router.back()
otherwise -> router.replace("/")这样登录流程像由当前任务唤起的 modal,同时也允许冷启动直接进入登录路由。
版本门禁
lib/app-update-config.ts 读取这些 env 配置:
EXPO_PUBLIC_MIN_APP_VERSION=
EXPO_PUBLIC_LATEST_APP_VERSION=
EXPO_PUBLIC_APP_UPDATE_URL=
EXPO_PUBLIC_APP_UPDATE_MESSAGE=
EXPO_PUBLIC_APP_UPDATE_CONFIG_URL=如果设置了 APP_UPDATE_CONFIG_URL,app 会拉取远程 JSON,并让远程配置覆盖 env
值。TanStack Query 缓存的 staleTime 是 10 分钟。
| 状态 | 规则 |
|---|---|
| 强制更新 | CURRENT_APP_VERSION < minVersion;显示不可关闭的 modal |
| 软更新 | CURRENT_APP_VERSION < latestVersion;显示可关闭 dialog |
| 不显示 | 没有配置、当前版本不旧,或打开 URL 失败 |
本地测试可以在 app.config.ts 仍为 1.0.0 时设置
EXPO_PUBLIC_MIN_APP_VERSION=2.0.0,重启 Metro 后打开 app。
协议同意门禁
LEGAL_VERSION 定义在 lib/legal.ts:
export const LEGAL_VERSION = "2026-04-30";ConsentProvider 把已接受版本存在:
legal.consent.v1版本变化后,老用户会再次看到提示。Legal 文档在 app/legal/ 下;外部法律链接来自
EXPO_PUBLIC_TERMS_URL、EXPO_PUBLIC_PRIVACY_URL 和 EXPO_PUBLIC_EULA_URL。
这是 legal consent gate,不是 analytics consent gate。当前 provider 顺序中,
AnalyticsProvider 在 ConsentProvider 之前挂载,所以只要配置了 PostHog key,
PostHog 就可以初始化并开始 screen tracking。
如果你的政策要求用户同意后才启动 analytics,请修改实现:在挂载 PostHog 前读取 consent,或让 isAnalyticsEnabled() 依赖一个已持久化的 analytics-consent 值。
生物识别锁
lib/biometric.tsx 使用 expo-local-authentication。它会检查硬件、是否已录入生物识别、支持类型,并把用户 opt-in 状态保存到:
cache.biometric.enabled.v1已登录用户启用后,app 会立即锁定;后台超过 30 秒再回到前台时也会重新锁定。BiometricGate 渲染在 <Stack /> 外,因此能覆盖所有当前页面。
这是 Native-only 功能,需要真机硬件或已录入的设备认证方式。
评分提示
评分不是 gate。lib/rating.ts 会在 onboarding 完成或分享邀请链接时累加 action
counter。只有满足以下条件才会请求系统评分:
actionCount >= 3
当前 app version 没有提示过
距离上次提示至少 90 天
expo-store-review 当前有可用 actionlib/store-review.web.ts 永远返回 false,所以 web 不会弹出评分提示。
自定义
- 删除某个 gate 时,从
app/_layout.tsx移除对应 provider state 和组件。 - 调整顺序要谨慎:redirect gate 适合放在
<Stack />前;必须覆盖当前路由的 overlay 适合放在<Stack />后。 - QA 时可以清空 app 本地 storage/cache,或临时做一个 debug screen 调 provider 的 reset helper。
- App Review 说明要和代码一致:账号删除在 Account & Security;legal consent 有版本;ATT 和这个 consent gate 是两回事。
