"如果你也是一名独立开发者,正在纠结要不要从Web端转向原生应用,这篇文章或许能帮你避开我踩过的那些坑。
"
一、为什么我要做一个儿童数学app
娃今年3岁,看了下市面上的学习类的游戏,都要收费,而且教的东西也简单,介于现在大模型的能力,打算自己做一个学习类的游戏来帮我家里的娃启蒙下数字哈
当时我就是抱着这种“天真”的想法入坑的。过程很是~折磨~
二、一开始的选型:Web端真的是捷径吗?
2.1 为什么选择HTML5
最开始我选择了HTML5,原因很简单:
- 开发快:一套代码同时跑在手机浏览器和微信里
- 发布容易:不用提交应用商店,域名一挂就能用
- 成本低:不用买证书,不用配置签名
我把游戏做成了H5页面形式,配上卡通风格的界面女儿确实玩得很开心。我还暗自得意:搞定了!
2.2 现实给了我当头一棒
但没过多久,问题就来了。
性能问题:女儿用的是iPad mini,滑动的时候明显卡顿,特别是动画稍微复杂一点就掉帧。
体验问题:H5不能全屏,每次浏览器地址栏显示出来就很出戏,而且退出浏览器再进来,数据全丢了。
痛定思痛,我决定做原生app。虽然自己压根是这方面的门外汉~~~
2.3 框架选型:为什么是React Native + Expo
摆在面前的有几条路:
我选择了React Native + Expo,原因就一个:能够复用我之前做Web前端的技术积累。
如果你也是从前端转过来的,我强烈推荐这个组合。React Native用的是React那一套,Expo又封装了大量原生API,开发体验和Web非常接近。
"这里我多说一句:选框架最重要的是团队能 hold 住。不要追新,不要追热、稳定和可维护才是第一位的。
"
三、踩坑实录:那些差点让我放弃的兼容性问题
好了,框架选定了。本以为接下来就是写代码堆功能的日子,结果现实教会了我什么叫做“too young too simple”。
坑一:SQLite数据库直接给了我一个下马威
项目做到一半,需要本地存储孩子的学习进度。我按照惯例引入了expo-sqlite,结果打包后在手机上直接崩溃。
Calltofunction'NativeDatabase.constructor' has been rejected.
Caused by: java.lang.NoSuchMethodError
查了很久才发现:expo-sqlite跟React Native 0.83+存在API不兼容,特别是开启了新架构(New Architecture)后直接罢工。
解决方案:对于简单的数据存储需求,直接用AsyncStorage就够了。代码从:
// 之前的写法
import * asSQLitefrom'expo-sqlite';
const db = SQLite.openDatabase('kids.db');
// 改成AsyncStorage
importAsyncStoragefrom'@react-native-async-storage/async-storage';
constCHILDREN_KEY = 'digitalparadise_children';
asyncfunctiongetChildren() {
const data = awaitAsyncStorage.getItem(CHILDREN_KEY);
return data ? JSON.parse(data) : [];
}
只有一句话:不要过度设计。简单场景用简单方案,SQLite适合复杂查询场景,但引入前务必验证版本兼容性。
坑二:React Native Reanimated版本对不上
做到动画部分,我想用react-native-reanimated实现丝滑的交互动画。结果安装完包,app直接白屏:
Error: Unable to resolve module react-native-reanimated
查了package.json,发现版本号写的是^3.16.0,但是跟React Native 0.76根本不兼容。
解决方案:锁定版本号,同时在babel.config.js里加上插件:
// babel.config.js
module.exports = {
plugins: [
'react-native-reanimated/plugin',
],
};
血的教训:所有依赖必须使用明确版本号,禁止使用+动态版本。每次引入新库,一定要运行npx expo doctor检查一遍。
坑三:Release构建正常,Debug也能跑,但安装APK就崩溃
这是最搞心态的一个坑。
本地测试Debug模式:完美运行。
EAS云构建的APK安装:开屏就崩,没有任何错误日志。
排查了一周,最后发现是Babel路径别名配置的问题:
// babel.config.js - 错误的配置
alias: {
'@': './src', // 不对!
'@theme': './src/theme',
}
// 正确的配置
alias: {
'@/': './src/', // 结尾必须有斜杠!
'@/theme': './src/theme',
}
Debug模式下Metro bundler解析策略比较宽松,所以即使配置错了也能跑。Release模式下Babel严格按配置解析路径,差一个斜杠就找不到模块。
一句话总结:Debug能跑不代表Release能跑,构建前务必用npx expo export --platform android在本地先跑一遍打包。
坑四:鸿蒙系统的WeakRef兼容性问题
终于解决了上述问题,APK能在我的安卓机上跑了。于是发给几个朋友测试,其中有个用华为P50 Pro(鸿蒙系统)的朋友直接崩溃:
Console Error
ReferenceError:Property'WeakRef' doesn't exist
查了半天才搞明白:鸿蒙系统的JavaScript引擎不完全支持ES2021特性。而Zustand状态管理库内部使用了WeakRef。
解决方案:安装polyfill
npm install @ungap/weakrefs
然后在应用入口处加上:
// app/_layout.tsx
if (typeofWeakRef === 'undefined') {
require('@ungap/weakrefs');
}
"这里提醒大家:做移动端应用,测试时一定要覆盖目标用户的设备类型。鸿蒙系统对Android APK的兼容性确实不如原生Android,多一些兼容性检查和polyfill不是坏事。
"
坑五:Gradle本地构建的那些坑
期间我也尝试过本地构建APK,结果:
Gradle build failed with unknownerror
Kotlin compile daemon 权限问题
各种权限问题、缓存问题、依赖冲突问题。删了重来,重来又挂。最后果断放弃本地构建,老老实实用EAS云构建。
"经验总结:本地构建环境配置复杂,容易出现各种奇奇怪怪的问题。EAS云构建更稳定,推荐用于生产环境。
"
坑六:包名不一致导致的安装失败
error: package com.digitalparadise does not exist
iOS的bundleIdentifier用的com.digitalparadise.app,Android的package用的com.anonymous.DigitalParadise,导致两边识别为不同的应用。
解决方案:在app.json里统一配置:
{
"expo":{
"ios":{
"bundleIdentifier":"com.digitalparadise"
},
"android":{
"package":"com.digitalparadise"
}
}
}
改名后,之前安装过的手机会认为是一个新应用,需要先卸载旧版本再安装
坑七:依赖版本不匹配
Command"expo doctor" failed
3 checks failed, indicating possible issues with the project
Expo SDK版本要对应正确的依赖版本,比如@expo/vector-icons要用~14.0.4、expo-av要用~15.0.2。
解决方案:记住两个命令:
# 自动检查并修复
npx expo install --check
# 安装依赖时自动选择兼容版本
npx expo install @expo/vector-icons
"强烈建议:每次改完依赖都跑一遍
"npx expo doctor,这真的能救你的命。
四、最终是怎么解决的:构建流程总结
说了这么多坑,我把最终跑通的构建流程整理出来:
第一步:开发阶段
# 安装依赖
npm install
# 启动开发服务器
npx expo start
# 用Expo Go扫码在手机上测试
第二步:构建前检查(非常重要)
# 检查依赖兼容性
npx expo doctor
# 自动修复依赖
npx expo install --check
# 本地测试打包(必须!)
npx expo export--platform android
第三步:EAS云构建
# 配置EAS(首次)
eas build:configure
# 构建APK(内部测试用)
eas build --platform android --profile preview
# 构建AAB(发布到Google Play)
eas build --platform android --profile production
第四步:下载安装
构建完成后在Expo网站下载APK,或者通过邮件/二维码分享给测试人员。
五、我的几点感悟
1. 技术选型要务实
不要追新,不要追热。我在选框架时反复问自己:这个技术我能 hold 住吗? 能就上,不能就换。不要为了一点性能提升引入团队驾驭不了的技术。
2. 小步快跑,持续验证
不要等功能做完了再打包测试。每个功能完成后都在真机上跑一遍Release模式,有问题马上发现,不要攒到最后一并爆炸。
3. 兼容性问题要趁早暴露
真机测试要覆盖目标用户的所有设备类型。早发现问题比晚发现好一百倍。
4. 文档和检查清单真的能救命
我把所有的坑都整理成了一份Checklist,每次发版前逐项检查。这份文档从我第一次崩溃开始维护,持续更新到现在。
"这里特别感谢那份参考的《Android项目开发规范》,里面的“分层防御体系”给了很大启发:不要信任任何单一环节,把“真机 Release 能运行”作为每次代码变更的验收标准。
"
六、写在最后
以上就是一个儿童数学app的开发总结,过程确实踩坑无数,但最终做出来了。
基于AI开发做简单的程序分分钟,但是真正做到生产级的就会遇到大量的debug时间,如果上下游链路很长,其实AI这方面做的并不好,往往80%的时间在测试优化中~~~
如果你也在做类似的事情,欢迎交流。
感兴趣的可以关注我,免费提供数字乐园app
夜雨聆风