微信小程序云开发实战:从零构建个人记账应用
简介:这是一份面向微信小程序初学者与财务类应用开发者的实战型源码资源,聚焦轻量级个人记账工具的完整实现,解决日常收支记录、分类统计与可视化分析等核心需求。压缩包共41个文件,含9个JS逻辑文件(处理记账、登录、图表渲染等业务)、7个WXML页面结构文件、8个WXSS样式文件、7个JSON配置文件,辅以5张界面PNG图及README说明文档,整体仅45KB,结构精简、开箱即用。已有324人学习下载,适合通过小而全的项目快速掌握小程序页面路由、云开发数据库设计、ECharts图表集成、微信授权登录及本地缓存优化等关键技术点。目录清晰呈现标准小程序架构:pages分页管理、app.js全局生命周期控制、utils工具函数封装,以及wxcharts.js等第三方图表适配方案,是理解微信生态下轻应用开发范式的优质入门样本。
1. 项目缘起:为什么从零开发一个记账小程序?
几年前,我还在用各种记账App,从随手记到挖财,再到手机自带的备忘录。但用久了总感觉差点意思:要么功能太臃肿,开屏广告烦人;要么数据同步不放心,总担心隐私;要么就是自定义程度不够,不符合我的记账习惯。后来微信小程序火了,我就在想,能不能自己做一个?纯粹、简单、完全掌控数据,还能顺便把小程序开发这套技术栈摸透。
这个想法一直搁置着,直到有一次需要给团队内部做一个简单的费用报销记录工具,我才真正动手。没想到,这个自用的“玩具”项目,经过几次迭代,竟然成了一个功能比较完整、代码结构也还算清晰的个人记账小程序。今天,我就把这个项目的核心源码和开发思路彻底拆开,揉碎了讲给你听。这不是一个简单的Demo,而是一个经历了真实需求打磨、包含完整前后端交互、数据管理以及诸多细节处理的实战项目复盘。无论你是想学习小程序开发,还是想拥有一个完全属于自己的记账工具,甚至是想了解如何将一个想法一步步落地成可用的产品,这篇文章都会给你带来实实在在的收获。
我们将围绕“微信记账小程序”这个核心,从项目初始化、数据库设计、前后端逻辑,一直讲到云开发部署和那些官方文档里不会写的“坑”。你会发现,开发一个能用的工具,远不止调用几个API那么简单。
2. 技术选型与项目初始化:为什么是“小程序+云开发”?
在动手之前,技术栈的选择决定了后续开发的效率和天花板。对于个人开发者或小团队来说,微信小程序配合它的云开发能力,是目前实现“记账”这类轻量级工具最快、最省心的方案。
2.1 核心架构决策
首先,我们得明确记账小程序的核心需求: 数据增删改查(CRUD) 、 用户隔离 、 多端同步 、 低成本运维 。基于这几点,我排除了几种方案:
- 纯前端方案(LocalStorage) :数据存在用户手机本地,无法多端同步,换设备或清除缓存就全没了,不适合记账这种需要长期留存的数据。
- 自建后端服务器(如Node.js + MySQL + 云服务器) :这是最灵活的方案,但也是成本(金钱和时间)最高的。你需要租服务器、配置环境、编写全套后端API、考虑安全性和运维,对于个人项目来说,投入产出比太低。
- 小程序云开发 :这是微信官方提供的“开箱即用”的BaaS(后端即服务)方案。它集成了云函数(无需管理服务器)、云数据库(JSON数据库,类似MongoDB)、云存储和用户鉴权。 最关键的是,它提供了一个免费且足够个人项目使用的资源配额 。
所以,我最终的选择是: 微信小程序 + 小程序云开发 。云开发解决了服务器、数据库和文件存储的问题,让我可以专注于小程序前端的业务逻辑和用户体验。云数据库的JSON格式也非常适合记账这种结构相对灵活的数据。
2.2 项目初始化与目录结构规划
创建小程序项目时,在开发者工具中务必勾选“小程序·云开发”。这会自动生成一个带有云开发能力的模板项目。但模板的目录结构比较基础,为了项目的可维护性,我进行了重新规划。一个清晰的结构是后期迭代不混乱的保障。
miniprogram/
├── pages/ # 页面文件
│ ├── index/ # 首页(账单列表/统计)
│ ├── add-record/ # 新增记账页
│ ├── category-manage/ # 分类管理页
│ └── mine/ # 我的页面(设置、关于)
├── components/ # 自定义组件
│ ├── record-card/ # 账单卡片组件
│ ├── date-picker/ # 自定义日期选择器
│ └── chart/ # 简单图表组件(用于统计)
├── cloudfunctions/ # 云函数目录
│ ├── addRecord/ # 新增记录
│ ├── getRecords/ # 获取记录(支持分页、筛选)
│ ├── updateRecord/ # 更新记录
│ ├── deleteRecord/ # 删除记录
│ ├── aggregateData/ # 数据聚合(用于统计)
│ └── initDatabase/ # 初始化数据库(创建集合、索引)
├── miniprogram_npm/ # 小程序 npm 包存放目录
├── images/ # 图片资源
├── styles/ # 公共样式
├── utils/ # 工具函数
│ ├── util.js # 通用工具(日期格式化、金额计算)
│ ├── auth.js # 用户登录授权封装
│ └── request.js # 网络请求封装(调用云函数)
├── app.js # 小程序入口文件
├── app.json # 全局配置
├── app.wxss # 全局样式
└── project.config.json # 项目配置文件
为什么这样规划?
- pages/按功能模块划分 :每个页面职责单一,便于管理。
add-record独立出来,避免首页过于复杂。 - components/封装复用UI :账单卡片在列表页和详情页都可能用到,抽成组件;图表和日期选择器也是高频复用部件。
- cloudfunctions/按业务拆分 :一个云函数只做一件事(单一职责原则)。比如
aggregateData专门处理复杂的统计查询,避免主云函数逻辑臃肿。initDatabase用于首次部署时自动创建数据库集合和索引,非常实用。 - utils/集中管理工具 :将常用的函数封装起来,比如
formatTime、checkLogin,避免代码重复,也利于统一修改。
注意 :云函数目录
cloudfunctions需要右键选择“上传并部署:所有文件”或“上传并部署:云端安装依赖”,才能在云端生效。本地调试时,需要在app.js中正确初始化云环境。
3. 数据模型设计:如何构建一个灵活且高效的账单数据库?
数据库设计是项目的基石。一个好的设计能让你后续的查询、统计功能事半功倍;一个糟糕的设计则会让你在代码里到处写“补丁”。微信云开发使用的是云数据库,它本质是一个JSON文档数据库,设计思路和传统的关系型数据库(如MySQL)有所不同,更注重文档的自我完备性和查询的灵活性。
3.1 核心集合(Collection)设计
我主要设计了两个核心集合: records (账单记录)和 categories (分类)。用户信息则直接使用云开发自带的 openid 进行关联。
1. records 集合(账单记录) 这是最主要的数据集合。每条记录代表一笔收支。
{
“_id”: “自动生成的文档ID”,
“_openid”: “自动注入的用户唯一标识”, // 云开发自动添加,用于数据隔离
“amount”: 125.50, // 金额(单位:分,避免浮点数精度问题)
“type”: 1, // 类型:1-支出, 2-收入
“category”: “餐饮”, // 分类名称
“categoryIcon”: “food”, // 分类图标标识
“date”: “2023-10-27”, // 日期,格式YYYY-MM-DD,便于按日聚合查询
“time”: “19:30”, // 时间(可选)
“remark”: “和同事聚餐”, // 备注
“account”: “微信零钱”, // 账户(如现金、银行卡、支付宝、微信)
“tags”: [“聚餐”, “同事”], // 标签,用于更细粒度的筛选
“createTime”: “2023-10-27T11:30:00.000Z”, // 创建时间(云数据库自动时间戳)
“updateTime”: “2023-10-27T11:30:00.000Z” // 更新时间
}
字段设计解析与避坑经验:
-
amount存为整数(分) :这是处理金额的黄金准则。用元为单位存储浮点数(如125.5)在进行加减乘除时,可能会遇到经典的JavaScript浮点数精度问题(如0.1+0.2 !== 0.3)。存为分(12550),所有计算都在整数层面进行,最后显示时再除以100,从根本上杜绝精度错误。 -
date字段单独存储 :为什么不只用createTime?因为createTime是标准的ISO时间戳,而按“年-月-日”进行聚合查询(例如“查询2023年10月的总支出”)在云数据库中操作相对繁琐。单独存储一个格式化的date字符串,可以极大地简化查询语句,例如直接使用.where({ date: db.RegExp({ regexp: ‘^2023-10’ }) })来匹配十月份的所有记录,性能更好。 -
tags使用数组 :云数据库支持数组查询。这样你可以轻松地找出所有带有“聚餐”标签的支出,或者同时有“聚餐”和“同事”标签的记录,非常灵活。 -
_openid是安全生命线 :云开发会自动在小程序端调用数据库时注入当前用户的_openid。在设置数据库权限时,务必设置为“仅创建者可读写”,这样每个用户只能看到和操作自己的数据,实现了天然的、无需额外代码的用户数据隔离。 这是云开发在安全方面最大的优势之一,千万不要在权限上犯错。
2. categories 集合(分类) 分类需要支持用户自定义,因此也需要存储在云端。
{
“_id”: “自动生成的文档ID”,
“_openid”: “用户标识”,
“name”: “餐饮”,
“icon”: “food”, // 对应前端图标库的标识
“type”: 1, // 1-支出分类, 2-收入分类
“budget”: 200000, // 月度预算(单位:分),可选
“color”: “#FF9500”, // 分类颜色,用于图表展示
“order”: 1, // 排序权重
“isDefault”: true // 是否为系统默认分类,用户不可删除
}
设计思路:
-
icon字段存储标识符 :前端根据这个标识符映射到具体的图标组件或图片,这样更换图标样式只需改前端,无需改动数据库。 -
budget预算功能 :为分类设置预算,是实现“预算控制”功能的基础。可以在统计时,对比该分类的实际支出与预算。 -
isDefault系统分类 :首次进入小程序时,可以通过一个云函数为用户初始化一套默认的分类(如餐饮、交通、购物等)。这些默认分类的isDefault为true,在前端管理页面禁止用户删除,但允许修改名称和图标,保证了用户体验的完整性。
3.2 数据库索引与查询优化
当账单记录越来越多(比如超过1000条),查询速度可能会变慢。合理的索引是提升性能的关键。
对于 records 集合,我创建了以下复合索引:
-
{ _openid: 1, date: -1 }:这是最核心的索引。用户查看账单列表时,最常见的操作就是“查看我自己的、按时间倒序排列的记录”。这个索引能极大加速这个查询。 -
{ _openid: 1, type: 1, date: -1 }:用于加速按类型(收入/支出)筛选的查询。 -
{ _openid: 1, category: 1, date: -1 }:用于加速按分类筛选的查询。
如何创建索引? 你可以在云开发控制台的数据库管理页面手动创建,但更推荐的方式是写在 initDatabase 云函数里,在项目首次部署时自动执行。这样可以保证任何拉取代码的新队友,或者在新环境部署时,数据库结构都是一致的。
// cloudfunctions/initDatabase/index.js
const cloud = require(‘wx-server-sdk’)
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
exports.main = async (event, context) => {
const db = cloud.database()
try {
// 创建 records 集合(如果不存在)
await db.createCollection(‘records’)
// 在 records 集合上创建索引
await db.collection(‘records’).createIndex({
name: ‘idx_openid_date’,
unique: false,
key: {
_openid: 1,
date: -1
}
})
// … 创建其他索引和 categories 集合
return { success: true, message: ‘数据库初始化成功’ }
} catch (e) {
console.error(e)
return { success: false, message: e.errMsg }
}
}
踩坑提醒 :云数据库的索引有数量限制(免费版5个),且创建后不能修改,只能删除重建。所以设计阶段就要想好常用的查询模式。索引不是越多越好,维护索引本身也有开销。通常优先为
where条件中的字段和orderBy中的字段建立复合索引。
4. 前端页面与交互实现:从列表展示到流畅记账
前端是小程序的门面,核心目标是: 操作简单、反馈及时、数据清晰 。我主要聚焦三个核心页面:首页(账单列表/统计)、新增记账页、分类管理页。
4.1 首页:智能列表与可视化统计
首页承载了两个主要功能: 账单流水列表 和 数据统计概览 。如何让它们和谐共存且不显杂乱?
技术实现:
- 页面布局与滚动监听 :我使用了
scroll-view组件来承载长列表,并监听其滚动事件。当用户向下滚动浏览流水时,顶部的统计卡片区域可以逐渐收起或固定,节省屏幕空间,聚焦于列表内容。 - 账单列表组件化 :将每条账单记录抽象成
record-card组件。这个组件接收一条record数据作为属性,内部负责渲染图标、分类、金额、备注等信息。这样做的好处是,列表页和未来可能有的详情页都可以复用这个组件,样式和逻辑统一。 - 下拉刷新与上拉加载 :这是列表页的标配。利用
Page生命周期里的onPullDownRefresh和onReachBottom方法。下拉刷新调用云函数重新拉取最新数据;上拉加载则通过分页查询实现。// 分页查询示例 async loadMoreRecords() { if (this.data.isLoading || !this.data.hasMore) return this.setData({ isLoading: true }) const db = wx.cloud.database() const { data } = await db.collection(‘records’) .where({ …this.data.filterConditions }) // 筛选条件 .orderBy(‘date’, ‘desc’) .skip(this.data.list.length) // 跳过已加载的数据 .limit(20) // 每次加载20条 .get() this.setData({ list: this.data.list.concat(data), isLoading: false, hasMore: data.length === 20 // 如果拉不满20条,说明没数据了 }) } - 统计数据的计算 :首页顶部的“本月支出/收入”、“分类占比”等数据,如果每次打开首页都实时从所有记录中聚合计算,对于数据量大的用户会非常慢。我的优化策略是:
- 定时任务(云函数) :创建一个定时触发的云函数(每天凌晨执行),为每个用户计算前一天的汇总数据,并存入一个单独的
statistics集合。首页直接读取这个预计算好的结果,速度极快。 - 前端缓存 :将统计结果在小程序本地
Storage中缓存一段时间(如10分钟),在缓存有效期内,直接从本地读取,避免频繁调用云函数。
- 定时任务(云函数) :创建一个定时触发的云函数(每天凌晨执行),为每个用户计算前一天的汇总数据,并存入一个单独的
4.2 新增记账页:极速输入的秘诀
记账的核心痛点是“快”。用户可能在支付完成的瞬间就要记录,任何多余的操作步骤都会导致放弃。我的设计目标是: 3秒内完成一次记账 。
交互与实现细节:
- 金额输入优化 :使用
<input type=“digit”>聚焦数字键盘。并监听输入事件,实时格式化显示(如输入“12345”,显示为“123.45”),给予用户即时反馈。 - 分类选择 :这是高频操作。我放弃了常见的弹窗选择,采用了“横向滑动分类栏”。将用户常用的分类(可通过算法根据使用频率排序)放在最前面,用户只需左右滑动点击即可选中,比弹窗少了一步点击。选中时,图标和文字有放大效果,体验更佳。
- 日期与账户选择 :默认日期为当天,账户为上次使用的账户。大部分情况用户无需修改。点击后才弹出
picker组件进行选择。 - 备注与标签 :备注输入框放在底部。标签功能则采用了“快捷标签”的设计,在输入框上方显示几个预设标签(如“早餐”、“网购”),点击即可添加,同样是为了减少键盘输入。
- 智能保存与连续记账 :保存成功后,不是直接返回首页,而是清空金额和备注,分类和账户保持不动,日期自动跳到下一笔(如果是连续记录同类消费,如买菜)。同时给出一个“继续记账”的按钮和一个“返回首页”的按钮。这个小小的交互改进,对记录流水式消费(如逛超市)体验提升巨大。
背后的云函数(addRecord): 前端页面收集好数据后,调用 addRecord 云函数。这个函数要做几件事:
- 验证数据完整性(如金额必须大于0)。
- 将金额从“元”转换为“分”存储。
- 补充
_openid(云函数端可通过cloud.getWXContext()获取)和服务器时间。 - 执行数据库插入操作。
- (可选)更新该分类的当月累计支出,用于实时预算提醒。
4.3 分类管理页:灵活可配是长久使用的关键
固定的分类无法满足所有人。一个好的记账工具必须允许用户自定义分类。
实现要点:
- 列表展示与排序 :从云数据库拉取用户的分类列表,按
order字段和type(支出/收入)分组展示。支持拖拽排序(可以使用movable-view组件实现),排序后更新每个分类的order值并同步到云端。 - 新增与编辑 :点击“新增”或某个分类,进入编辑页面。这里要注意 图标选择器 的实现。我预先定义了一套图标库(可以是字体图标或图片),以网格形式展示,用户点击即可选择。图标标识(如
food)存入数据库。 - 删除的谨慎处理 :删除一个分类时,不能简单地删除数据库记录,因为可能已有历史账单属于这个分类。我的处理逻辑是:
- 如果是用户自定义的分类(
isDefault: false),询问用户如何处理关联账单:“删除分类及所有相关账单”或“将相关账单转移到其他分类”。 - 如果是系统默认分类(
isDefault: true),则不允许删除,但可以修改名称和图标。这样保证了数据的一致性,不会出现“孤儿”账单。
- 如果是用户自定义的分类(
5. 云函数进阶:处理复杂逻辑与提升性能
云函数是小程序云开发的“大脑”,所有复杂的、安全的或需要集中处理的逻辑都应该放在云函数里,而不是小程序前端。
5.1 聚合查询:如何高效生成月度报表?
首页的统计卡片、单独的报表页面,都需要对大量账单记录进行聚合计算,例如“计算用户A在2023年10月的总支出、总收入,以及按分类的支出排行”。
如果在前端用JavaScript循环用户的所有记录来计算,数据量稍大就会卡顿,且浪费用户流量。正确的做法是在云函数中使用数据库的 聚合操作(Aggregate) 。
// cloudfunctions/aggregateData/index.js - 获取月度统计
const cloud = require(‘wx-server-sdk’)
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
exports.main = async (event, context) => {
const { year, month } = event // 前端传入,如 2023, 10
const wxContext = cloud.getWXContext()
const db = cloud.database()
const _ = db.command
const startDate = `${year}-${month.toString().padStart(2, ‘0’)}-01`
const endDate = … // 计算下个月的第一天,用于范围查询
try {
const result = await db.collection(‘records’)
.aggregate()
.match({ // 阶段1:筛选数据
_openid: wxContext.OPENID,
date: _.gte(startDate).and(_.lt(endDate))
})
.group({ // 阶段2:分组聚合
_id: ‘$type’, // 按 type 字段分组
totalAmount: $.sum(‘$amount’) // 对每组的 amount 求和
})
.end()
// result 格式如: [ { _id: 1, totalAmount: 350000 }, { _id: 2, totalAmount: 80000 } ]
// 分别对应支出总和和收入总和(单位:分)
// 更复杂的聚合,例如按分类统计支出
const categoryResult = await db.collection(‘records’)
.aggregate()
.match({
_openid: wxContext.OPENID,
date: _.gte(startDate).and(_.lt(endDate)),
type: 1 // 只统计支出
})
.group({
_id: ‘$category’,
total: $.sum(‘$amount’)
})
.sort({
total: -1 // 按金额降序排列
})
.limit(10) // 取前10
.end()
return {
success: true,
data: {
summary: result,
categoryRank: categoryResult
}
}
} catch (e) {
console.error(e)
return { success: false, message: e.errMsg }
}
}
为什么用聚合? 聚合管道在数据库服务端执行,速度快,且只将最终计算结果返回给前端,数据传输量极小。相比之下,前端拉取全部数据再计算,是极其低效的做法。
5.2 定时触发:自动化数据维护
有些任务不需要用户触发,应该自动执行。云函数支持定时触发器。
应用场景1:每日数据备份与统计预计算 创建一个云函数,配置为每天凌晨3点触发(此时用户活跃度低)。
- 遍历所有用户(可以通过遍历
records集合中不同的_openid实现,需注意分批处理),计算他们前一天的收支总额。 - 将结果写入
daily_statistics集合,用于生成历史趋势图表。 - 更新
monthly_statistics集合,用于首页快速展示。
应用场景2:月度预算重置与提醒 每月1号凌晨触发一个云函数。
- 将所有用户的分类预算“实际支出”字段清零(或开始新的计算周期)。
- 检查上月是否有分类超支,如果有,生成一条待推送消息(可结合小程序订阅消息,在合适时间推送给用户)。
配置定时触发器: 在云函数目录下创建 config.json 文件。
// cloudfunctions/dailyBackup/config.json
{
“triggers”: [
{
“name”: “dailyStats”,
“type”: “timer”,
“config”: “0 0 3 * * * *” // 每天的3:00 AM执行,Cron表达式
}
]
}
5.3 安全与权限:永远不能忽视的防线
云开发虽然简化了后端,但安全思维不能丢。所有云函数都必须假设前端传入的数据是不可信的。
- 输入校验 :在云函数开头,严格校验传入参数的类型、范围。例如,金额必须是正数,日期格式必须正确。
if (typeof event.amount !== ‘number’ || event.amount <= 0) { return { success: false, message: ‘金额无效’ } } - 权限校验 :即使数据库设置了“仅创建者可读写”,在云函数中执行更新或删除操作时,也应再次验证当前用户是否有权操作这条数据。例如,在
updateRecord函数中,先查询该记录是否存在且_openid与当前用户匹配,再进行更新。 - 防止循环调用与资源耗尽 :云函数有运行时间和内存限制。在处理批量操作或循环时,一定要设置合理的上限。例如,给用户初始化默认分类时,如果分类数量很多,要分批插入。
6. 部署、优化与后期迭代思考
将代码开发完只是第一步,让它稳定、高效地运行,并持续改进,才是项目成功的关键。
6.1 首次部署与初始化流程
- 上传云函数 :在微信开发者工具中,右键点击
cloudfunctions目录下的每个云函数文件夹,选择“上传并部署:云端安装依赖”。 - 运行初始化函数 :手动调用一次
initDatabase云函数,创建数据库集合和索引。可以在小程序内做一个隐藏的“开发者选项”页面来触发,或者直接在云开发控制台的“云函数”模块中测试调用。 - 配置订阅消息 :如果需要有超支提醒等功能,需要在微信公众平台申请相应的订阅消息模板,获取
templateId,并在云函数中调用cloud.openapi.subscribeMessage.send接口发送。 要注意用户必须授权订阅,且每条模板有发送限额 。 - 体验版测试 :上传代码为体验版,邀请朋友进行真实场景测试,重点测试不同网络环境下的加载速度、数据同步是否一致、核心流程是否有BUG。
6.2 性能优化点
- 图片与图标优化 :所有图标尽量使用字体图标(IconFont)或SVG,避免使用PNG/JPG小图,减少请求数量和体积。如果必须用图片,务必用工具压缩(如TinyPNG)。
- 数据缓存策略 :对变化不频繁的数据,如分类列表、用户设置,在小程序启动时拉取一次,存入
globalData或Storage,后续直接从本地读取。可以设置一个简单的过期逻辑,比如每天强制更新一次。 - 列表虚拟滚动 :当账单记录非常多时(比如超过500条),一次性渲染所有
record-card组件会导致页面卡顿。可以考虑实现简单的虚拟滚动,只渲染可视区域及附近的少量条目。不过,小程序本身的scroll-view性能尚可,在配合分页加载的情况下,千条以内的数据压力不大,可根据实际情况决定是否引入更复杂的方案。 - 云函数冷启动 :云函数在不被调用一段时间后会进入“冷状态”,再次调用时会有几百毫秒到几秒的启动延迟。对于
getRecords这种高频函数,可以通过定时每5分钟触发一次空调用来“保活”,但这会消耗调用次数。需要权衡免费额度与用户体验。
6.3 可能的迭代方向
一个基本的记账小程序完成后,还可以从这些方向深化:
- 多账本功能 :允许用户创建“家庭账本”、“旅行账本”、“装修账本”等,数据隔离。这需要在
records和categories集合中增加一个book_id字段,并新增一个books集合来管理账本。 - 账单图片附件 :利用云存储,上传消费小票或截图。云开发提供了前端直传云存储的能力,注意做好图片压缩和防盗链设置。
- 数据导出与分析 :提供将账单导出为Excel或CSV文件的功能。可以在云函数中利用
node-xlsx这类库生成文件,然后返回文件ID供用户下载。更高级的,可以集成简单的数据分析,生成消费趋势图、消费习惯报告等。 - 预算与提醒 :基于分类预算,实现更智能的提醒。不仅是月度超支提醒,还可以有“本周餐饮消费过快”的预警。
- 数据可视化升级 :引入更强大的图表库(如
echarts-for-weixin),绘制月度消费趋势曲线、分类占比环形图、收支对比柱状图等,让数据更直观。
开发这个小程序的过程,对我来说是一次完整的产品思维和技术实践的融合。从最初的一个简单想法,到考虑数据模型、用户体验、性能安全,再到一步步编码实现、测试部署,每一个环节都有值得深究的细节。最大的体会是: 真正的开发,大部分时间不是在写新代码,而是在设计、调试和优化 。希望这份详细的源码解读和实战心得,能帮你少走一些弯路,更快地打造出属于你自己的、好用的小程序工具。
更多推荐
所有评论(0)