去年我把用了三年的记账 App 卸了,理由很朴素:它每次打开都要先看五秒广告,记一笔账要跳三个页面。后来索性自己写了一个,从画草图到上架用了两个周末。这篇就把整个过程拆开讲,算是一份能照着做的 app制作教程,重点放在「怎么用最少的东西把功能跑通」,而不是堆技术名词。
先想清楚:你要解决的是哪一口气
独立开发最容易死在第一步——想法太大。我见过不少人一上来就说要做「全能效率平台」,结果三个月后连登录页都没画完。我的建议是先写下三句话:
- 谁会用:只有我自己,还是身边的朋友也会用?
- 它替我干掉什么麻烦:比如「记账时少点两次屏幕」。
- 不做什么:这条最重要。我的记账工具明确不做多账本、不做云同步、不做图表分析。
把「不做什么」写下来,后面的技术选型会轻松很多。功能边界越窄,第一版能跑起来的速度就越快。
技术选型:别为了学新东西拖慢进度
如果你只做 iOS,SwiftUI 是最省事的;如果希望一套代码同时跑在 iOS、Android 和微信小程序上,那就选跨端方案。我这次用的是「小程序 + 云开发」的组合,原因很实际:不用买服务器、不用配域名、审核流程也熟。对个人开发者来说,前期最大的成本不是代码,是运维和合规。
数据库就用云开发自带的文档型数据库,一张集合存账单,字段控制在五个以内:金额、分类、备注、时间、是否支出。字段少,后面改结构也不心疼。
页面结构:三个页面就够
记账工具的核心链路其实很短:看列表 → 点新增 → 填完保存。所以我只做了三个页面:
- 首页:本月支出总额 + 最近账单列表。
- 新增页:金额键盘、分类选择、备注输入。
- 统计页:按分类聚合的简单条形图。
页面跳转用最普通的导航,不做花哨的转场动画。第一版的目标是「能用」,不是「好看」。
核心代码:新增一笔账单
新增逻辑没什么玄机,关键是做两件事:本地先渲染、失败再回滚。这样用户点保存后立刻能看到结果,网络慢也不会觉得卡。
// 新增账单:先更新本地列表,再写数据库
async function addBill(bill) {
const tempId = 'tmp_' + Date.now();
const optimistic = { ...bill, _id: tempId, pending: true };
// 1. 本地先插入,界面立即响应
this.billList.unshift(optimistic);
try {
// 2. 写入云数据库
const res = await db.collection('bills').add({
data: {
amount: Number(bill.amount),
category: bill.category,
note: bill.note || '',
createdAt: db.serverDate(),
isExpense: bill.isExpense
}
});
// 3. 用真实 id 替换临时 id
const idx = this.billList.findIndex(b => b._id === tempId);
if (idx > -1) {
this.billList[idx] = { ...optimistic, _id: res._id, pending: false };
}
} catch (err) {
// 4. 失败就移除,并提示用户
this.billList = this.billList.filter(b => b._id !== tempId);
wx.showToast({ title: '保存失败,请重试', icon: 'none' });
console.error('addBill failed:', err);
}
}
这段代码里有个容易忽略的点:amount 一定要做 Number() 转换。用户输入的是字符串,直接存进去,后面求和会得到 "12" + "8" = "128" 这种离谱结果。我第一版就踩过,统计页金额大得吓人。
金额输入用自定义键盘
系统数字键盘在部分机型上会带「完成」按钮,遮挡布局。我干脆自己画了一个九宫格键盘,顺便把「+」「-」切换支出收入的按钮塞进去,减少一次点击。键盘按钮的点击事件里只做字符串拼接,最后统一 parseFloat,避免浮点误差在输入阶段就出现。
统计页:别急着上图表库
很多人一提到统计就想到引入 ECharts,其实分类占比用几个 view 加宽度百分比就能画出来。我的做法是查出本月账单后,在 JS 里按分类累加,算出每类占比,再渲染横向条:
// 按分类聚合本月支出
function groupByCategory(list) {
const map = {};
list.filter(b => b.isExpense).forEach(b => {
map[b.category] = (map[b.category] || 0) + Number(b.amount);
});
const total = Object.values(map).reduce((s, n) => s + n, 0) || 1;
return Object.keys(map).map(k => ({
name: k,
amount: map[k],
percent: Math.round((map[k] / total) * 100)
})).sort((a, b) => b.amount - a.amount);
}
渲染时把 percent 拼到 style 的宽度上就行。数据量小的时候,这种土办法比图表库快得多,包体积也省了。
发布前必须做的三件小事
- 空状态:新用户第一次打开,列表是空的,要有引导文案,不然会以为坏了。
- 金额格式化:统一保留两位小数,超过一万加千分位,看起来才像正经工具。
- 误删保护:删除操作加二次确认,或者做成可撤销的提示条。我自己误删过两次,痛过才记得加。
写在最后
这个小工具上线半年,日活一直是个位数,但我每天都在用。对个人开发者来说,第一个作品最大的价值不是用户量,而是把「想法 → 可运行 → 可发布」这条链路完整走了一遍。走通一次之后,第二个、第三个就会快很多。如果你也在犹豫要不要动手,建议今晚就把第一个页面画出来,剩下的问题,写着写着就有答案了。
评论 (0)
还没有评论,快来抢沙发吧~