App开发中期实战复盘:Hermes Studio从0到50%的技术路线与部署经验

发布时间:2026/8/30 8:17:26
App开发中期实战复盘:Hermes Studio从0到50%的技术路线与部署经验 先说结论Hermes Studio App 的开发进度已经推进到 50%。这个节点听起来很刺激但也最危险——功能完成了一半架构基本定型剩下的另一半要面对联调、测试、打包、上架、真机兼容这些硬仗。这篇文章会把前半程的选型、模块拆分、核心代码、后端部署、常见坑点全部整理出来同时给出从 50% 到 100% 的推进路线。无论你是在开发自己的第一款 App还是正在统筹团队项目这篇实战复盘都能直接用上。文中提到的 Hermes Studio 是团队内部对这款 App 的产品代号与 React Native 内置的 Hermes 引擎不是一个概念。读者在搜索资料时要注意区分避免被关键词干扰。1. 项目背景Hermes Studio App 开发进度过半先复盘再冲刺1.1 50% 进度线是怎么界定的很多开发团队在评估 App 开发进度时习惯用“页面数量”来算百分比这是很危险的。页面做完了不代表功能可用功能可用了不代表能上线。Hermes Studio App 的 50% 进度是基于下面这套标准来评估的核心业务链路已经跑通从启动 App 到完成登录再到首页数据加载和关键操作端到端流程已经没有人肉 mock 数据。前后端接口契约基本冻结接口字段不再每天变联调进入收尾阶段。架构与基础能力已经固化网络请求层、错误处理、日志上报、本地缓存这些基础能力已经封装完成。也就是说这 50% 不是“一半页面做完了”而是50% 的工程能力和 40% 的业务功能叠加后的综合进度。接下来的开发重心会从“搭骨架”转向“填血肉”。1.2 这篇博客适合谁正在开发自己的第一款 App想知道从 0 到 50% 到底要做哪些事。团队项目进入中期需要一份可参考的技术路线和排坑清单。后端开发想了解移动端联调和部署的常见问题。读完你会得到一份完整的 App 中期实战笔记包含 React Native TypeScript 的前端代码、Node.js Express 的后端接口示例、MySQL 建表脚本、宝塔面板部署步骤以及 8 个高频报错的排查思路。2. 技术选型与开发环境2.1 技术栈总览Hermes Studio App 的技术栈选择遵循一个原则团队熟悉度优先其次才是技术热度。最终方案如下模块技术选型选型理由移动端React Native TypeScript一套代码同时支持 iOS 和 AndroidTypeScript 减少联调期低级错误前端构建React Navigation Zustand导航和全局状态管理方案成熟社区案例多后端服务Node.js Express与前端语言统一开发效率高数据库MySQL 8.0团队运维经验丰富事务支持可靠部署环境宝塔面板 Nginx中小团队快速部署、可视化管理成本低代码管理Git GitFlow多成员并行开发时的分支隔离这里要特别说明React Native 新版本默认启用 Hermes 引擎这里的 Hermes 是 Meta 开源的 JavaScript 引擎用于提升 App 启动速度和减少内存占用与本文的 Hermes Studio App 项目无关。如果读者在搜索“Hermes Studio”时看到大量 React Native 相关内容不要混淆。2.2 环境准备清单开发环境的版本需要根据你的项目实际情况调整本文示例以常见环境为例重点演示配置思路。操作系统Windows 10/11 或 macOS 12Node.js建议 18 LTS 以上JDKAndroid 构建需要 JDK 17Android Studio用于创建模拟器和打包 APKXcode仅 macOS 用户需要用于 iOS 真机调试宝塔面板部署后端服务本文示例使用 Ubuntu 22.04 服务器如果你使用的是更高版本的 Node 或不同的操作系统命令可能略有差异但整体思路不变。2.3 项目结构规划项目结构在开发第三天就冻结了后续没有再大规模调整。前端目录结构如下hermes-studio-app/ ├── src/ │ ├── api/ # 接口请求封装 │ ├── components/ # 公共组件 │ ├── pages/ # 页面 │ ├── router/ # 路由配置 │ ├── store/ # 全局状态管理 │ ├── utils/ # 工具函数 │ └── App.tsx # 应用入口 ├── android/ # Android 原生目录 ├── ios/ # iOS 原生目录 ├── package.json └── tsconfig.json这个目录结构的核心价值是request 层和页面层完全解耦。后续 50% 的开发里新增页面只需要往 pages 里加文件不需要动其他模块。3. 已完成的核心模块从 0 到 50% 做了哪些事3.1 启动页与基础导航启动页的作用不只是品牌展示更重要的是在等待原生模块初始化时给用户一个稳定的视觉反馈。导航方案使用 React Navigation底部 Tab 栏包含首页、工作台、消息、我的四个入口。核心配置如下// 文件路径src/router/index.tsx import { NavigationContainer } from react-navigation/native; import { createBottomTabNavigator } from react-navigation/bottom-tabs; import HomePage from ../pages/HomePage; import WorkbenchPage from ../pages/WorkbenchPage; import MessagePage from ../pages/MessagePage; import MinePage from ../pages/MinePage; const Tab createBottomTabNavigator(); export default function AppRouter() { return ( NavigationContainer Tab.Navigator screenOptions{{ tabBarActiveTintColor: #2F6BFF, headerShown: false, }} Tab.Screen nameHome component{HomePage} / Tab.Screen nameWorkbench component{WorkbenchPage} / Tab.Screen nameMessage component{MessagePage} / Tab.Screen nameMine component{MinePage} / /Tab.Navigator /NavigationContainer ); }这里有一个容易被新手忽略的点tabBarActiveTintColor 设置的是图标和文字的激活颜色如果只设置了 header 的样式底部 Tab 还是默认蓝色视觉上会显得很突兀。3.2 登录与 Token 管理登录模块是整个 App 的命门。我们的实现思路是首次启动进入登录页登录成功后拿到 access_token。access_token 设置 2 小时过期配合 refresh_token 实现无感刷新。所有请求通过拦截器自动携带 token401 时自动刷新并重放请求。Token 存储没有使用 AsyncStorage而是使用了 react-native-keychain因为AsyncStorage 存储的敏感信息在 Android 上存在明文风险Keychain/Keystore 更安全。// 文件路径src/api/token.ts import * as Keychain from react-native-keychain; export async function saveToken(token: string) { await Keychain.setGenericPassword(access_token, token); } export async function getToken(): Promisestring | null { const credentials await Keychain.getGenericPassword(); if (credentials) { return credentials.password; } return null; } export async function clearToken() { await Keychain.resetGenericPassword(); }3.3 首页数据看板与接口联调首页是一个数据看板需要请求后端聚合接口。这一步真正把所有模块串起来了页面发起请求 → 请求层加 token → 后端校验 → 返回数据 → 页面渲染。// 文件路径src/api/request.ts import axios from axios; import { getToken, clearToken } from ./token; const request axios.create({ baseURL: https://api.hermes-studio.example.com, timeout: 15000, }); request.interceptors.request.use(async (config) { const token await getToken(); if (token) { config.headers.Authorization Bearer ${token}; } return config; }); request.interceptors.response.use( (response) response.data, (error) { if (error.response?.status 401) { clearToken(); // 跳转到登录页并提示用户重新登录 } return Promise.reject(error); } ); export default request;接口联调阶段最主要的经验是前后端一定要先定契约再开发。我们使用 Swagger 管理接口定义前端根据 Swagger 自动生成 TypeScript 类型减少了大量字段对齐问题。3.4 本地缓存与离线策略移动端网络环境不稳定本地缓存不是可选项而是必选项。目前的缓存策略是首页数据缓存 5 分钟详情页数据强制走网络。用户无网络时优先展示缓存数据并提示“当前网络不可用”。// 文件路径src/utils/cache.ts import AsyncStorage from react-native-async-storage/async-storage; const CACHE_PREFIX hermes_cache_; const DEFAULT_EXPIRE 5 * 60 * 1000; export async function setCache(key: string, data: any, expire DEFAULT_EXPIRE) { const wrapped { data, timestamp: Date.now() expire, }; await AsyncStorage.setItem(CACHE_PREFIX key, JSON.stringify(wrapped)); } export async function getCache(key: string): Promiseany | null { const raw await AsyncStorage.getItem(CACHE_PREFIX key); if (!raw) return null; const wrapped JSON.parse(raw); if (Date.now() wrapped.timestamp) { await AsyncStorage.removeItem(CACHE_PREFIX key); return null; } return wrapped.data; }缓存过期时间必须根据业务灵活调整。如果你把缓存写死成统一时长会出现“列表已经过期但详情还在展示旧数据”的奇怪问题。4. 完整实战示例登录模块开发全流程这一节我们从零写一个完整可运行的登录模块。虽然 Hermes Studio App 的登录模块已经完成但把它拆开讲透可以覆盖 50% 阶段最有代表性的技术点。4.1 初始化 React Native 项目使用 React Native Community CLI 创建项目npx react-native-community/clilatest init HermesStudioApp --template react-native-template-typescript项目创建后安装核心依赖cd HermesStudioApp npm install react-navigation/native react-navigation/native-stack npm install react-native-screens react-native-safe-area-context npm install axios zustand react-native-keychain npm install react-native-async-storage/async-storage安装完成后iOS 项目还需要执行cd ios pod install如果是 Android 项目注意在 android/build.gradle 中确认 compileSdkVersion 与本地 SDK 版本匹配。4.2 后端登录接口实现后端示例使用 Node.js Express数据库使用 MySQL。先看接口代码// 文件路径server/app.js const express require(express); const mysql require(mysql2/promise); const jwt require(jsonwebtoken); const bcrypt require(bcryptjs); const app express(); app.use(express.json()); const pool mysql.createPool({ host: 127.0.0.1, user: hermes_user, password: your_secure_password, database: hermes_studio, waitForConnections: true, connectionLimit: 10, }); app.post(/api/login, async (req, res) { const { username, password } req.body; if (!username || !password) { return res.status(400).json({ code: 400, message: 用户名和密码不能为空 }); } const [rows] await pool.execute( SELECT id, username, password FROM users WHERE username ?, [username] ); if (rows.length 0) { return res.status(401).json({ code: 401, message: 用户不存在 }); } const user rows[0]; const isPasswordValid await bcrypt.compare(password, user.password); if (!isPasswordValid) { return res.status(401).json({ code: 401, message: 密码错误 }); } const token jwt.sign( { userId: user.id, username: user.username }, process.env.JWT_SECRET || hermes_secret_dev, { expiresIn: 2h } ); res.json({ code: 0, data: { token, user: { id: user.id, username: user.username }, }, }); }); app.listen(3000, () { console.log(Hermes Studio API server running at http://127.0.0.1:3000); });接口逻辑分为五步参数校验 → 查询用户 → 校验密码 → 生成 token → 返回结果。这里要强调两个安全点密码绝对不允许明文存储示例中使用 bcryptjs 加密。JWT_SECRET 不允许硬编码在代码里开发环境可以使用环境变量生产环境必须使用密钥管理服务。4.3 前端登录页面实现登录页需要处理三种状态输入中、请求中、请求失败。Zustand 管理登录状态// 文件路径src/store/auth.ts import { create } from zustand; interface AuthState { token: string | null; user: { id: number; username: string } | null; setAuth: (token: string, user: { id: number; username: string }) void; logout: () void; } export const useAuthStore createAuthState((set) ({ token: null, user: null, setAuth: (token, user) set({ token, user }), logout: () set({ token: null, user: null }), }));登录页面组件// 文件路径src/pages/LoginPage.tsx import React, { useState } from react; import { View, TextInput, TouchableOpacity, Text, StyleSheet, Alert } from react-native; import request from ../api/request; import { saveToken } from ../api/token; import { useAuthStore } from ../store/auth; export default function LoginPage({ navigation }: any) { const [username, setUsername] useState(); const [password, setPassword] useState(); const [loading, setLoading] useState(false); const setAuth useAuthStore((state) state.setAuth); const handleLogin async () { if (!username.trim() || !password.trim()) { Alert.alert(提示, 用户名和密码不能为空); return; } setLoading(true); try { const response: any await request.post(/api/login, { username: username.trim(), password, }); const { token, user } response.data; await saveToken(token); setAuth(token, user); navigation.replace(Home); } catch (error: any) { const message error.response?.data?.message || 登录失败请稍后重试; Alert.alert(登录失败, message); } finally { setLoading(false); } }; return ( View style{styles.container} Text style{styles.title}Hermes Studio/Text TextInput style{styles.input} placeholder用户名 value{username} onChangeText{setUsername} autoCapitalizenone / TextInput style{styles.input} placeholder密码 value{password} onChangeText{setPassword} secureTextEntry / TouchableOpacity style{[styles.button, loading styles.buttonDisabled]} onPress{handleLogin} disabled{loading} Text style{styles.buttonText}{loading ? 登录中... : 登 录}/Text /TouchableOpacity /View ); } const styles StyleSheet.create({ container: { flex: 1, justifyContent: center, paddingHorizontal: 32, backgroundColor: #F5F7FA, }, title: { fontSize: 28, fontWeight: 600, textAlign: center, marginBottom: 48, color: #2F6BFF, }, input: { height: 48, borderWidth: 1, borderColor: #D9DEE6, borderRadius: 8, paddingHorizontal: 16, marginBottom: 16, backgroundColor: #FFFFFF, }, button: { height: 48, borderRadius: 8, backgroundColor: #2F6BFF, alignItems: center, justifyContent: center, marginTop: 8, }, buttonDisabled: { opacity: 0.6, }, buttonText: { color: #FFFFFF, fontSize: 16, fontWeight: 500, }, });这段代码包含了移动端开发最常见的逻辑表单校验、请求封装、状态管理、路由跳转、loading 状态控制。导航到首页时使用 navigation.replace 而不是 navigation.navigate这样用户按返回键不会回到登录页。4.4 MySQL 建表与初始化数据登录功能依赖用户表建表语句如下-- 文件路径server/init.sql CREATE DATABASE IF NOT EXISTS hermes_studio DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE hermes_studio; CREATE TABLE IF NOT EXISTS users ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL, password VARCHAR(100) NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_username (username) ) ENGINEInnoDB;插入一条测试用户数据。注意密码字段是 bcrypt 加密后的结果不要直接写明文INSERT INTO users (username, password) VALUES (admin, 加密后的密码字符串);在测试环境生成加密密码可以使用 Node.js 脚本// 文件路径server/hashPassword.js const bcrypt require(bcryptjs); async function generateHash() { const password your_password; const hash await bcrypt.hash(password, 10); console.log(hash); } generateHash();生产环境绝对不允许使用这种初始化方式这个脚本只用于本地测试。生产用户注册流程必须走正式接口并且要加上验证码、频率限制等防护手段。4.5 启动后端服务并验证登录启动 MySQL 并执行 init.sql 后启动后端服务cd server npm install express mysql2 jsonwebtoken bcryptjs node app.js验证接口curl -X POST http://127.0.0.1:3000/api/login \ -H Content-Type: application/json \ -d {username: admin, password: your_password}预期返回结果{ code: 0, data: { token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..., user: { id: 1, username: admin } } }到这里一个完整的登录链路就通了。在 Hermes Studio App 的开发过程中这一步花的时间最长因为除了写代码还要配置 Android 模拟器和 iOS 模拟器的网络访问权限。Android 模拟器访问宿主机要使用 10.0.2.2iOS 模拟器直接使用 localhost这个差异很容易踩坑。5. 后端服务部署基于宝塔面板的实践开发环境跑通后下一步就是把后端服务部署到服务器。项目中期我们选择了宝塔面板因为团队规模不大宝塔的可视化管理能显著降低运维成本。宝塔面板部署 Hermes Studio 后端服务的过程适合没有专门运维人员的团队参考。5.1 安装 Node.js 与 PM2在宝塔面板的“软件商店”中安装 Node.js 版本管理器建议安装 Node 18 LTS。后端服务需要使用 PM2 实现进程守护防止服务崩溃后无法自动恢复npm install -g pm2在项目目录下启动服务pm2 start app.js --name hermes-studio-api pm2 save pm2 startupPM2 的save和startup命令确保服务器重启后服务能自动拉起。这是生产环境最基本的高可用措施不要直接使用node app.js挂后台。5.2 配置 Nginx 反向代理后端服务默认监听 3000 端口外部请求统一通过 Nginx 转发。在宝塔面板中新建站点并修改配置server { listen 443 ssl; server_name api.hermes-studio.example.com; # SSL 证书配置省略使用宝塔申请或上传正规证书 location /api/ { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }使用 Nginx 反向代理而不是直接暴露 3000 端口原因有三点可以复用 443 端口的 HTTPS 证书。方便后续做负载均衡和流量控制。减少应用进程直接暴露在公网的风险。5.3 MySQL 数据库初始化在宝塔面板中创建数据库hermes_studio并创建独立用户hermes_user不要直接使用 root 账号连接数据库。权限只授予需要的数据库CREATE USER hermes_userlocalhost IDENTIFIED BY your_secure_password; GRANT ALL PRIVILEGES ON hermes_studio.* TO hermes_userlocalhost; FLUSH PRIVILEGES;这里有一个安全原则最小权限原则。如果应用只需要读写hermes_studio库就不要授予 GRANT OPTION 或访问其他库的权限。5.4 生产环境密钥管理部署到生产环境时必须修改所有默认密钥和密码。重点检查JWT_SECRET环境变量是否设置为强随机字符串。数据库连接密码是否与开发环境不同。是否关闭了后端服务的 debug 日志输出。创建环境变量文件# 文件路径server/.env JWT_SECRETa_strong_random_secret_which_is_hard_to_guess DB_HOST127.0.0.1 DB_USERhermes_user DB_PASSWORDyour_production_password DB_NAMEhermes_studio然后修改 app.js使用 dotenv 加载配置// 文件路径server/app.js 顶部添加 require(dotenv).config(); const pool mysql.createPool({ host: process.env.DB_HOST, user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, waitForConnections: true, connectionLimit: 10, });生产环境不要使用开发环境的 JWT_SECRET否则攻击者可以签发任意 token 访问接口。6. 常见问题与排查思路50% 开发阶段团队遇到了一批典型问题。本节整理 8 个高频问题的排查清单。问题现象常见原因解决思路iPhone 安装 App 提示 minimumosversion too lowApp 的 iOS 最低版本设置高于手机系统版本在 Xcode 中调整 Deployment Target或升级测试机系统版本macOS 提示需要从“macOS 恢复”启动并将安全策略更改为“完整安全”应用未签名或开发者证书不受系统信任测试阶段在系统设置中允许对应开发者来源运行提示 you will lose access to this app in 5 days开发者账号或配置文件即将过期登录 Apple Developer 后台续期更新描述文件Android 模拟器无法访问后端接口模拟器使用 10.0.2.2 而不是 localhost将 baseURL 改为 http://10.0.2.2:3000抓包工具看不到 App 的 HTTPS 请求未安装抓包工具的 CA 证书在测试设备上安装并信任根证书接口返回 401 但 token 未过期前后端刷新逻辑未适配多端并发使用 refresh_token 队列合并刷新请求真机运行出现网络慢或请求超时真机与服务器不在同一网络或防火墙未放行检查服务器安全组和 Nginx 日志启动时提示 unlicensed software使用了非授权开发工具或设计协作工具统一使用正版授权或更换开源替代工具6.1 minimumosversion too low 报错这个报错的完整信息类似The app has a minimumOSVersion of 13.0. Starting iOS 13.0...含义是 App 设置的最低系统版本高于当前设备的系统版本。常见场景是团队为了使用新 API 把 Deployment Target 改高了但测试机系统版本没跟上。排查步骤在 Xcode 里查看项目的 Deployment Target。对比真机的系统版本。两个方案二选一降低 Deployment Target或升级真机系统。不要盲目降低 Deployment Target。如果代码里已经使用了高版本 API降低后会出现运行时崩溃。6.2 抓包失败的排查思路App 联调阶段抓包是一种基础技能。如果在 Android 上抓包失败按以下顺序排查检查代理设置手机和电脑必须处于同一局域网。检查目标端口HTTP 默认 8888HTTPS 需要额外安装 CA 证书。检查 Android 7.0 对用户证书的限制需要在 AndroidManifest 中配置 networkSecurityConfig允许调试环境信任用户证书。抓包工具建议使用 Fiddler Classic 或 Charles不要同时开启多个工具的代理。避免在正式生产环境进行抓包测试在测试环境完成联调后再切换生产环境。6.3 开发者证书过期问题热词中的you will lose access to this app in 5 days是 iOS 开发者证书即将过期的典型提示。这类问题一旦出现要尽早处理iOS 开发者账号续费。重新生成描述文件。在 Xcode 中更新签名配置。证书过期不是代码问题但会导致所有测试设备无法安装新版本。开发到 50% 阶段最容易忽视这类“看似与代码无关”的问题实际影响却非常大。7. 从 50% 到 100%下一半开发路线规划开发进度到 50% 时最忌讳的事情是“想到什么做什么”。我们基于当前进度整理了后半程的优先级。7.1 功能开发阶段完成剩余页面与业务逻辑下一阶段要完成的功能模块包括消息中心的会话列表与通知推送。工作台的表单填写与提交。个人中心的资料编辑与设置项。功能开发阶段的核心原则是尽量复用已经封装好的组件和工具。如果一个页面需要新的网络请求先检查 request.ts 是否满足需求如果需要新的弹窗先看现有组件能否扩展。7.2 测试阶段功能测试优先性能测试跟进在 50% 进度之前测试主要靠开发自测。从 50% 到 80% 的阶段必须引入系统化测试功能测试覆盖核心链路包括登录、数据加载、异常网络、弱网环境。兼容性测试至少覆盖 iOS 15/16/17 三个版本和 Android 10/11/12/13 四个版本。性能测试重点关注启动耗时、页面切换帧率、内存占用。React Native 项目可以使用 React DevTools 的 Profiler 分析组件渲染性能。如果开发阶段不做性能优化到上架前再优化成本会成倍增加。7.3 提审阶段iOS 审核与 Android 上架的合规准备这一阶段最容易出现突发问题。iOS 提审前建议自查用户隐私政策是否完整展示。是否有隐藏功能或调试入口。内容是否符合 App Store《App Store 审核指南》。测试账号是否需要后台配置。Android 应用在国内上架时需要准备软件著作权证书、隐私政策、安全评估报告等材料。不同渠道要求不同建议提前咨询目标应用商店的开发者文档。合规审查的时间成本往往比开发成本更不可控一定要留出提前量。7.4 上线后的监控与迭代App 上线不是终点而是新起点。上线后的第一周要关注崩溃率是否在合理范围一般低于 0.5%。启动失败率是否有异常。后端接口的 P95 响应时间。用户反馈中最集中的问题和功能诉求。从 50% 到 100% 的推进过程中每完成一个阶段都要重新评估进度百分比避免出现“做到最后发现还剩 30%”的偏差。8. 最佳实践与工程建议8.1 版本管理与分支规范团队使用 GitFlow 分支模型核心分支是develop和main/release。功能分支从develop拉出命名格式为feature/模块名例如feature/login。关键实践禁止直接向 main 分支提交代码。每次合并前至少由一人 Code Review。发布时从develop创建release分支修复问题后合并回main和develop。8.2 配置隔离与环境切换前端项目至少需要三个环境开发环境、测试环境、生产环境。在src/config/env.ts中统一管理// 文件路径src/config/env.ts export const ENV_DEV { apiBaseUrl: http://10.0.2.2:3000, debug: true, }; export const ENV_TEST { apiBaseUrl: https://test-api.hermes-studio.example.com, debug: true, }; export const ENV_PROD { apiBaseUrl: https://api.hermes-studio.example.com, debug: false, };根据打包配置选择环境变量绝对不要在生产包中打印完整的请求日志这会泄漏业务数据。8.3 安全边界与数据合规App 开发必须把安全和合规放在重要位置用户密码使用 bcrypt 加密存储禁止明文。接口请求使用 HTTPS禁止明文 HTTP 传输敏感数据。涉及用户隐私的权限定位、相册、麦克风必须实现申请、说明用途、用户拒绝后的降级方案。遵守数据安全相关法律法规不要采集与业务无关的用户数据。8.4 日志与异常监控50% 阶段就应该把日志体系建立起来不要等到上线前再补。推荐方案前端使用 Sentry 采集崩溃日志和自定义事件。后端使用 PM2 日志 独立的日志服务。日志中不要记录 token、密码、身份证号等敏感信息。// 文件路径src/utils/logger.ts export function logError(context: string, error: unknown) { if (__DEV__) { console.error([HermesStudio] ${context}, error); } else { // 生产环境上报到 Sentry 等服务 } }这里的__DEV__是 React Native 提供的全局变量开发环境为 true生产打包后为 false。用它来控制日志输出是很常见的做法。9. 写在最后进度 50% 之后的下一步行动Hermes Studio App 的 50% 进度是一个重要的里程碑。这个阶段我们完成了技术选型、项目结构搭建、登录链路、核心数据流和后端部署方案也踩过了证书过期、模拟器网络不通、抓包失败这些基础坑。接下来的开发重心应该放在三件事上优先补齐核心业务功能不要沉浸在重构代码的舒适区。把测试前置到每个功能完成节点不要攒到最后统一测试。尽早准备上架合规材料避免功能完成后被审核流程卡住。如果你也正处在一个 App 项目的开发中期希望这篇复盘能帮到你。开发进度条更重要的是让你清楚自己在哪里、接下来要做什么而不是数字本身。现阶段最推荐你做的是把你自己的 App 项目结构画一张图标出哪些模块已完成、哪些还在联调、哪些完全没开始。你会发现清晰的进度评估比盲目写代码更让团队安心。动手整理吧剩下的 50% 会比你想象的更快到来。