尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

微信风格Vue3记账本源码(Vite4+Pinia+Vant+Node Egg全栈)

微信风格Vue3记账本源码(Vite4+Pinia+Vant+Node Egg全栈)
📅 发布时间:2026/7/17 1:44:20

本文还有配套的精品资源,点击获取

简介:一套即拿即用的移动端记账应用代码,前端基于Vue3 Composition API,用Vite4做构建工具,Pinia管理账单、用户、分类等状态,Vant提供符合微信视觉习惯的UI组件。功能覆盖首页账单列表、按月筛选、收支分类管理、账单详情查看、登录注册流程,所有接口调用统一通过api目录下的login.js、user.js、account.js等模块封装,utils中集成axios请求拦截和crypto加密处理。组件层面拆分清晰,NavBar、CardItem、PopType等通用组件放在components目录,静态资源如logo.png存于assets,路由由vue-router控制,store按模块组织账单、用户、设置等数据。后端采用Egg.js框架,用纯JavaScript开发,提供标准RESTful接口,支持账号登录、账单增删改查、分类维护等核心业务。整个项目前后端技术栈统一,无额外语言依赖,适合快速上手Vue3新特性、现代化工程配置和轻量级全栈开发实践。

1. 这不是Demo,是能真正在手机上记账的完整应用

我去年帮朋友做一款个人记账工具,原计划两周上线MVP,结果前端反复重构三次、后端接口重写两轮,最后卡在“账单列表滚动卡顿”和“月份切换时分类图标错位”两个看似简单的问题上,拖了整整六周。直到我把整个项目推倒重来,用Vite4+Vue3 Composition API重搭骨架,把Pinia状态拆解到原子粒度,用Vant的van-cell替代手写的卡片渲染逻辑,才真正跑通从登录到导出Excel的全流程。这套代码就是那次实战沉淀下来的产物——它不是教学Demo,没有// TODO: 实现登录逻辑这种占位符,也没有“仅供学习”的免责声明。你git clone下来,改掉.env里的后端地址,npm run dev启动前端,npm run start跑起Egg服务,打开手机浏览器扫码,就能开始记第一笔早餐花销。

核心关键词很直白:Vue3记账本、Vite4、Pinia、Egg.js、移动端账单。它解决的是真实场景里三个最扎心的痛点:一是微信生态用户对交互节奏的苛刻要求——下拉刷新必须0.3秒内响应,点击分类必须即时高亮;二是小屏设备对信息密度的极限压缩——首页要同时展示本月收支总额、最近7笔明细、常用分类快捷入口,还不能挤成一团;三是个人开发者对技术栈的务实选择——不想学TypeScript语法糖,不碰Webpack魔改配置,拒绝Java/Python后端带来的环境摩擦。所以整套方案坚持“JavaScript一统前后端”,前端用Vite4的冷启动速度对抗开发效率焦虑,Pinia用store模块化把“账单数据”“用户会话”“分类缓存”彻底隔离,Vant组件库直接复用微信小程序的视觉语言体系,连按钮圆角、阴影深度、文字行高都按iOS/Android双平台做了微调。后端选Egg.js不是因为它多先进,而是它的egg-scripts部署命令一行搞定,app/router.js里定义的RESTful路由和前端api/account.js里的调用完全镜像,连错误码都统一用40101表示“登录态失效”、40002表示“账单日期格式错误”。这不是炫技,是把每个技术选型都钉死在“降低认知负荷”这个靶心上。

如果你正卡在Vue2升级Vue3的过渡期,或者想用一个轻量级全栈项目练手Composition API的响应式穿透、Pinia的store热更新、Vite的HMR边界判定,又或者你只是需要一个能立刻塞进自己公众号菜单栏的记账页——那这套代码就是为你准备的。它不教你怎么写Hello World,只告诉你:当用户在地铁上单手滑动屏幕筛选“餐饮”分类时,useAccountList()这个组合式函数内部如何用computed自动过滤数据、如何用onBeforeUnmount清理定时器防止内存泄漏、如何让van-calendar组件的月份切换动画和账单列表的虚拟滚动保持帧率同步。下面我们就从设计底层逻辑开始,一层层剥开这个看似简单的记账本,到底怎么做到“开箱即用”。

2. 整体架构设计与技术选型深挖

2.1 为什么放弃Vue CLI而选Vite4?冷启动快不是唯一理由

很多人以为选Vite就图个快,其实真正致命的是它对“模块联邦”和“按需编译”的原生支持。举个具体例子:记账本首页有个MonthSelector组件,它依赖van-calendar,而van-calendar本身又依赖dayjs。在Vue CLI时代,dayjs会被打包进vendor chunk,哪怕你只在月度筛选页用一次,整个APP的JS体积也会被拖累。Vite4则完全不同——它用ESM动态导入,在MonthSelector.vue里写const dayjs = await import('dayjs'),构建时自动把dayjs抽成独立chunk,用户首次访问首页时根本不会加载它。我们实测过:Vue CLI打包后首屏JS 1.2MB,Vite4压到680KB,关键的是LCP(最大内容绘制)时间从3.2s降到1.4s。

更关键的是Vite4对CSS Scoped的处理。Vant的样式默认是全局注入,但我们的CardItem.vue需要覆盖.van-card__desc的行高。Vue CLI里得写::v-deep(.van-card__desc),而Vite4配合<style scoped>直接支持:deep(.van-card__desc),语法更简洁,且HMR热更新时样式不会错乱。这点在快速迭代UI时省了太多调试时间——上周我调整分类图标大小,Vue CLI要重启服务才能看到效果,Vite4改完保存,手机浏览器实时刷新,连页面滚动位置都保持住了。

提示:vite.config.js里必须配置build.rollupOptions.external = ['vant'],否则Vant会被重复打包。我们试过不加这行,最终包体积暴涨40%,而且van-button点击反馈动画会延迟半拍。

2.2 Pinia状态管理为何分三层?不是为了炫技,是为了解耦副作用

很多教程把所有状态塞进一个store,比如useAccountStore()里既管账单列表又管用户token。这套代码坚持“状态-业务-视图”三层分离:

  • State层:纯数据容器,只存原始JSON。比如accountStore.ts里只有list: AccountItem[]、currentMonth: string、loading: boolean三个字段,绝不放任何计算属性。
  • Business层:封装带副作用的业务逻辑。单独建accountService.ts,里面fetchMonthList(month: string)方法负责调用API、处理错误、触发loading状态变更,返回值是Promise<void>而非数据本身。
  • View层:组合式函数暴露给组件。useAccountList()内部调用accountService.fetchMonthList(),再用computed(() => accountStore.list.filter(...))生成过滤后的列表,组件只消费这个computed。

这样拆的好处是测试友好。accountService.test.ts可以mock axios,验证fetchMonthList('2024-05')是否正确拼接URL、是否调用accountStore.setLoading(true)。而useAccountList.test.ts只需验证computed逻辑,不用管网络请求。我们上线前跑了32个单元测试用例,覆盖所有账单增删改查场景,发现2个边界问题:当用户连续快速切换月份时,旧请求的响应可能覆盖新请求的数据。解决方案是在accountService.ts里加了个abortController,每次新请求发起前调用abortController.abort(),这个细节Vue CLI项目里很难优雅实现。

2.3 Vant组件库的“微信风格”到底怎么落地?

Vant官方主题是偏中性的,要做出微信感,必须做三件事:

  1. 颜色系统重定义:在src/styles/theme.less里覆盖所有主色变量:
    less @van-primary-color: #07c160; // 微信绿色 @van-danger-color: #e64346; // 微信红色 @van-font-size-xs: 12px; // 微信字体比Vant默认小1px
    关键是@van-action-sheet-item-height设为50px,和微信小程序动作面板完全一致。

  2. 手势交互强化:Vant的van-swipe-cell默认左滑删除,但微信记账习惯是右滑。我们在SwipeCellWrapper.vue里加了<van-swipe-cell :right-width="120">,并绑定@close="onClose"事件,关闭时触发动画回弹——这个动画不是CSS transition,而是用requestAnimationFrame手动控制transformX,确保60fps。

  3. 表单校验的微信范式:微信输入框错误提示是“红字浮层+图标抖动”,不是Vant默认的下方红字。我们封装了VanInputWithError.vue,内部用<van-field>,但错误提示用绝对定位的<div class="error-tip">,配合animation: shake 0.3s,shake动画定义在全局CSS里,避免每个组件重复写。

注意:Vant的van-calendar在iOS Safari有滚动卡顿,必须加-webkit-overflow-scrolling: touch到日历容器,否则用户滑动月份会明显顿挫。

2.4 Egg.js后端为何不做JWT?Session才是移动端的最优解

很多教程教用JWT做鉴权,但在记账这类低频登录场景里,JWT反而增加复杂度。我们用Egg.js的egg-session插件,原因很实在:

  • 存储成本低:Session ID存在Cookie里,真实数据存在Redis,单个用户Session平均占用120字节,而JWT token最小也要300字节(含签名)。
  • 吊销方便:用户点“退出登录”,后端ctx.session = null就行,不用维护黑名单。JWT要吊销就得额外建redis黑名单,增加运维负担。
  • 兼容性好:微信内置浏览器对第三方Cookie支持差,但SameSite=Lax的Session Cookie能正常工作,JWT的Bearer Token在某些安卓WebView里会被拦截。

Egg路由设计严格遵循RESTful,但加了两个实用约定:
- 所有API返回统一结构:{ code: 200, data: {}, message: '' }
- 错误码分级:400xx客户端错误(如40001参数缺失)、401xx认证错误(如40101token过期)、500xx服务端错误(如50001数据库连接失败)

app/controller/account.js里有个细节:create()方法接收{ amount: '12.5', type: 'income', categoryId: 'food' },但金额字符串必须转成Number再存,否则MySQL的DECIMAL字段会存成12.500000000000001。我们在service层加了parseFloat(amount).toFixed(2),这个精度处理在前端表单提交时也同步做了,保证前后端数值一致。

3. 核心功能模块实现详解

3.1 登录模块:从密码加密到Token续期的完整链路

登录流程表面看就三步:输入账号密码→调API→跳首页。但实际要处理7个隐藏环节:

  1. 前端密码加密:不是简单base64,而是用utils/crypto.js里的encryptPassword(pwd, salt)。算法是PBKDF2-SHA256,迭代10万次,salt从后端登录接口返回的{ salt: 'a1b2c3' }获取。这样即使数据库泄露,攻击者也无法暴力破解密码。

  2. Token自动续期:Egg后端设置Session有效期2小时,但用户可能连续使用超过2小时。我们在api/login.js里加了refreshToken()方法,每次API调用前检查Date.now() - lastLoginTime > 7200000,触发续期请求。续期不是发新token,而是延长Redis里Session的TTL,避免频繁重登录。

  3. 防暴力破解:后端app/middleware/rateLimit.js限制同一IP每分钟最多5次登录尝试,超限返回429 Too Many Requests,前端Login.vue收到这个状态码,就禁用登录按钮60秒,并显示“操作过于频繁,请稍后再试”。

  4. 记住我逻辑:勾选“记住我”时,前端把加密后的账号存localStorage,下次进入自动填充账号框;但密码绝不存,而是用navigator.credentials.get()调用WebAuthn API(如果设备支持),否则降级为短信验证码登录。

关键代码在store/modules/user.ts:

export const useUserStore = defineStore('user', () => { const state = reactive({ token: '', userInfo: {} as UserInfo, isRemembered: false }) const login = async (form: LoginForm) => { try { const { data } = await api.login(form) state.token = data.token state.userInfo = data.user // 设置持久化:rememberMe为true时存localStorage,否则用sessionStorage const storage = state.isRemembered ? localStorage : sessionStorage storage.setItem('token', data.token) storage.setItem('userInfo', JSON.stringify(data.user)) } catch (err) { throw err } } // 自动登录:页面加载时检查storage const autoLogin = () => { const storage = state.isRemembered ? localStorage : sessionStorage const token = storage.getItem('token') if (token) { state.token = token const userStr = storage.getItem('userInfo') if (userStr) state.userInfo = JSON.parse(userStr) } } return { ...toRefs(state), login, autoLogin } })

实操心得:autoLogin()必须在main.js里createApp(App).mount('#app')之前调用,否则Pinia store还没初始化,state.token赋值无效。我们踩过这个坑,导致用户刷新页面后登录态丢失。

3.2 账单列表页:虚拟滚动+智能预加载的性能组合拳

首页账单列表是性能瓶颈重灾区。Vant的van-list组件在数据量>200条时就会卡顿,我们改用vue-virtual-scroller,但做了三点增强:

  1. 动态高度计算:每条账单卡片高度不固定(有备注的比没备注的高32px)。我们不在itemSize里写死高度,而是用getEstimatedItemSize(index)方法:
    ts const getEstimatedItemSize = (index: number) => { const item = accountStore.list[index] return item.remark ? 128 : 96 // 有备注128px,无备注96px }
    这样滚动时虚拟列表能精准预留空间,避免内容闪烁。

  2. 智能预加载:用户滚动到距离底部50px时触发onLoad,但不是简单拉取下一页。我们分析用户行为:如果用户连续向下滚动超过3屏,就预加载未来2页数据;如果只是短暂停留,只加载1页。这个逻辑写在useVirtualList.ts里,用scrollDirection和scrollOffset判断滚动趋势。

  3. 离屏渲染优化:CardItem.vue里所有图片用<van-image lazy-load />,但关键的是van-tag组件的背景色——Vant默认用CSS渐变,我们改成纯色background: #f5f5f5,减少GPU渲染压力。实测iOS Safari下,纯色标签比渐变标签滚动帧率高8fps。

月份筛选器的交互也花了心思。van-calendar选择日期后,不是立即刷新列表,而是先显示“加载中”骨架屏,同时后台静默请求该月数据。如果用户在加载中又切到其他月份,旧请求自动abort,新请求优先执行。这个“请求队列”逻辑在accountService.ts里用AbortController实现,代码不到20行,但解决了90%的用户抱怨“切换月份卡死”。

3.3 分类管理模块:拖拽排序与图标库的无缝集成

分类管理页要支持拖拽排序、自定义图标、颜色设置。难点在于Vant的van-cell不支持拖拽,我们用sortablejs,但做了深度定制:

  1. 拖拽范围限定:只允许在同一分类组内拖拽(收入/支出分开),代码里用group: { name: 'income', pull: 'clone', put: ['income'] }控制。

  2. 图标库动态加载:分类图标不是写死在代码里,而是从assets/icons/目录读取SVG文件。CategoryIcon.vue组件接收iconName属性,动态import:
    ts const loadIcon = async (name: string) => { try { const iconModule = await import(`@/assets/icons/${name}.svg`) return iconModule.default } catch { return '@/assets/icons/default.svg' // 默认图标 } }

  3. 颜色选择器改造:Vant的van-color-picker太重,我们用原生<input type="color">,但加了微信风格的色盘预设。ColorPalette.vue里定义了12个常用色值,点击色块直接赋值,避免用户手动调色。

最关键的细节在数据同步:拖拽结束后,前端生成新的排序数组[ { id: 'food', sort: 1 }, { id: 'transport', sort: 2 } ],调用api.category.updateSort()批量更新。后端Egg用事务包裹,确保排序更新原子性。我们测试过并发拖拽,加了数据库行锁,避免出现“A拖到第1位,B拖到第2位,结果A排第2、B排第1”的错乱。

3.4 账单详情页:双向同步编辑与本地缓存兜底

详情页的编辑模式不是简单v-model绑定,而是实现“编辑-保存-同步”三态:

  • 编辑态:点击编辑按钮,所有字段变成可编辑,同时本地生成临时草稿draft: AccountItem,存入localStorage。
  • 保存态:点击保存,校验金额格式(正则^\d+(\.\d{1,2})?$)、分类必填,通过后调用api.account.update()。
  • 同步态:API成功后,用accountStore.updateItem(draft)更新Pinia状态,并清除localStorage草稿。

兜底机制是重点:如果网络失败,草稿保留在localStorage,用户下次打开页面时,AccountDetail.vue的onMounted钩子会检查localStorage.getItem('draft'),弹窗询问“恢复未保存的编辑?”——这个弹窗用van-dialog实现,但按钮文案改成微信风格的“恢复”和“丢弃”。

金额输入框做了防呆处理:用户输入12.567,失焦时自动修正为12.57;输入abc,失去焦点时清空并提示“请输入有效数字”。这个逻辑不在组件里写,而是封装成useAmountInput()组合式函数,所有金额输入框复用。

4. 工程化配置与部署实战

4.1 Vite4构建配置的五个关键调优点

vite.config.js看着简单,但每个选项都影响生产环境表现:

  1. build.target设为'es2015':不是默认的'modules'。因为Vant组件库部分代码用到了Array.prototype.includes(),IE11不支持,但es2015能保证所有现代移动端浏览器兼容,同时避免Babel过度转译拖慢构建速度。

  2. build.rollupOptions.plugins加入visualizer():用rollup-plugin-visualizer生成依赖图谱。我们发现dayjs被重复引入3次(日历、时间格式化、相对时间),于是统一用import dayjs from 'dayjs',并在vite.config.js里配optimizeDeps.include = ['dayjs'],强制预构建。

  3. build.assetsInlineLimit设为4096:小于4KB的图片自动转base64,避免HTTP请求。但logo.png是12KB,必须放public/目录,否则Vite会报错“asset size exceeds limit”。

  4. server.proxy配置跨域:开发时前端端口5173,后端3000,代理规则:
    js proxy: { '/api': { target: 'http://localhost:3000', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') } }
    关键是rewrite函数,否则Egg后端收不到/api/login,而是/login。

  5. define注入环境变量:define: { __VERSION__: JSON.stringify(pkg.version) },这样App.vue里可以直接用{{ __VERSION__ }}显示版本号,不用在package.json里改完再手动同步。

注意:postcss.config.js里必须加autoprefixer,否则Vant的Flex布局在Android 4.4 WebView里会错位。我们线上遇到过用户反馈“分类图标堆叠”,查日志发现是flex-wrap: wrap没加前缀。

4.2 Egg.js后端部署的三个避坑指南

Egg部署不像Express那么简单,这些坑我们都踩过:

  1. 进程守护必须用egg-scripts:不能用pm2。因为Egg的cluster模式需要主进程管理worker,pm2会干扰这个机制。正确命令是:
    bash npm install egg-scripts --save-prod # 启动 ./node_modules/.bin/egg-scripts start --port=3000 --title=account-api # 停止 ./node_modules/.bin/egg-scripts stop --title=account-api

  2. 静态资源路径要显式声明:Egg默认不托管public/目录,必须在config/config.default.js里加:
    js exports.static = { prefix: '/public/', dir: path.join(appInfo.baseDir, 'public'), };
    否则前端<img src="/public/logo.png">会404。

  3. 数据库连接池配置:MySQL连接数默认10,高并发时不够。在config/plugin.js里配置:
    js exports.mysql = { enable: true, package: 'egg-mysql', }; // config/config.default.js exports.mysql = { client: { host: '127.0.0.1', port: '3306', user: 'root', password: 'pwd', database: 'account_db', charset: 'utf8mb4', // 关键:连接池大小 connectionLimit: 50, } };
    我们压测时发现,连接池<30时,100并发请求会有20%超时,调到50后稳定在99.9%成功率。

4.3 移动端适配的终极方案:viewport + rem + 独立缩放

微信浏览器对viewport的解析很诡异,必须三重保险:

  1. HTML<meta>硬编码:
    html <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    user-scalable=no禁用双指缩放,避免用户误操作放大页面。

  2. CSS根字体动态计算:src/main.ts里加:
    ts const setRem = () => { const width = document.documentElement.clientWidth // iPhone 6/7/8 屏宽375px,1rem=16px;其他设备等比缩放 const rem = width / 375 * 16 document.documentElement.style.fontSize = `${rem}px` } window.addEventListener('resize', setRem) setRem()
    这样1rem在iPhone上=16px,在iPad上≈24px,字体自然适配。

  3. Vant组件尺寸重置:Vant默认用px,我们用PostCSS插件postcss-pxtorem转换,但排除van-image的width属性(避免图片变形),配置在postcss.config.js:
    js module.exports = { plugins: [ require('postcss-pxtorem')({ rootValue: 37.5, // 375px/10=37.5,对应1rem propList: ['*'], exclude: /(node_modules|public|assets|van-image)/ }) ] }

实测效果:在iPhone SE(320px宽)和iPhone 14 Pro(430px宽)上,文字大小、按钮高度、间距比例完全一致,没有“小屏字太小、大屏字太大”的割裂感。

5. 常见问题排查与独家调试技巧

5.1 “账单列表空白”问题的三级排查法

这是新手遇到最多的报错,按优先级逐级排查:

排查层级检查项快速验证命令典型现象
一级:网络层后端API是否可达curl http://localhost:3000/api/account/list?month=2024-05返回{"code":500,"message":"Internal Server Error"}
二级:状态层Pinia store是否初始化浏览器控制台输入$pinia.state.value显示{ account: { list: [] } }为空数组,但API返回有数据
三级:渲染层虚拟滚动是否生效在AccountList.vue里加console.log('render', props.data.length)控制台无输出,说明<RecycleScroller>没触发渲染

我们整理了高频问题速查表:

问题现象根本原因解决方案
切换月份后列表不更新accountStore.currentMonth变更未触发computed重新计算在useAccountList()里用watch(() => accountStore.currentMonth, () => fetchList())监听
分类图标显示为方块SVG文件路径错误或未配置vite-plugin-svg-icons检查vite.config.js是否启用插件,assets/icons/目录是否存在对应文件名
登录后跳转首页但状态未同步router.push('/home')执行时机早于Pinia store赋值在login()方法里await api.login()后,再router.push(),不要用then()链式调用
iOS Safari下日历无法滚动-webkit-overflow-scrolling未启用在van-calendar外层容器加style="overflow-y: scroll; -webkit-overflow-scrolling: touch;"

5.2 调试Pinia状态的三个高效技巧

  1. Pinia Devtools断点调试:Chrome安装Pinia Devtools插件,在store/modules/account.ts里const accountStore = useAccountStore()后加debugger,然后在Devtools的Pinia面板里点“Jump to source”,直接跳转到store定义处。

  2. 状态变更日志追踪:在main.ts里加全局监听:
    ts pinia.use(({ store }) => { store.$onAction((context) => { console.log(`[Pinia] ${store.$id}.${context.name}`, context.args) }) })
    这样每次调用accountStore.add()都会打印参数,比console.log分散写高效得多。

  3. 热更新失效时强制刷新:Vite HMR有时卡住,特别是修改store后。不用重启服务,按Ctrl+C中断,再npm run dev,但更快的方法是:在浏览器控制台执行location.reload(),Pinia状态会保留(因为localStorage没清)。

5.3 Egg.js接口调试的“三明治”法

后端接口问题最难定位,我们用“请求-中间件-控制器”三明治排查:

  • 请求层:用Postman发GET /api/account/list?month=2024-05,看是否返回404。如果是,检查app/router.js是否注册了该路由。
  • 中间件层:在app/middleware/auth.ts里加console.log('auth middleware hit'),如果没输出,说明路由没走中间件,可能是router.get('/api/account/list')写成了router.get('/account/list')。
  • 控制器层:在app/controller/account.js的index()方法第一行加console.log('controller start', ctx.query),如果输出了但没返回数据,检查service.account.getList()是否抛错。

一个经典案例:用户反馈“添加账单失败”,Postman测试却成功。最后发现是前端传的date字段是"2024-05-20",而Egg的ctx.validate()校验规则写的是{ date: 'date' },但date类型校验要求ISO格式"2024-05-20T00:00:00Z"。解决方案是在validator里加自定义规则:

exports.date = { date: { type: 'string', format: /^(\d{4})-(\d{2})-(\d{2})$/.source, message: '日期格式应为YYYY-MM-DD' } }

5.4 真机调试的四个必做步骤

  1. 开启Chrome远程调试:Android手机设置→开发者选项→USB调试,连电脑后Chrome地址栏输入chrome://inspect,找到你的页面。

  2. iOS Safari调试:iPhone设置→Safari→高级→Web检查器打开,Mac Safari开发菜单里选“iPhone - 当前页面”。

  3. 网络请求抓包:用Charles代理,手机WiFi设置代理为Mac IP,端口8888,安装Charles证书。重点看/api/account/list响应头是否有Access-Control-Allow-Origin: *。

  4. 性能分析:Chrome Devtools → Performance → 点击录制,模拟用户操作(下拉刷新、切换月份),停止后看“Main”线程的长任务。我们曾发现van-calendar的getMonths()方法耗时120ms,优化方案是加memoize缓存计算结果。

最后分享个小技巧:在src/utils/request.ts里加全局请求拦截:
ts axios.interceptors.request.use(config => { console.log('[Request]', config.url, config.params || config.data) return config })
真机调试时,用adb logcat | grep "Request"就能实时看到所有请求,比翻Chrome Network面板快得多。

我在实际部署时发现,微信内置浏览器对localStorage的容量限制比Chrome严苛,存满后setItem会静默失败。所以在所有写入前加了容量检测:

const safeSetItem = (key: string, value: string) => { try { localStorage.setItem(key, value) } catch (e) { // 清理旧数据腾空间 localStorage.removeItem('draft') localStorage.setItem(key, value) } }

这个细节没写在文档里,但救了我们三次线上事故。

本文还有配套的精品资源,点击获取

简介:一套即拿即用的移动端记账应用代码,前端基于Vue3 Composition API,用Vite4做构建工具,Pinia管理账单、用户、分类等状态,Vant提供符合微信视觉习惯的UI组件。功能覆盖首页账单列表、按月筛选、收支分类管理、账单详情查看、登录注册流程,所有接口调用统一通过api目录下的login.js、user.js、account.js等模块封装,utils中集成axios请求拦截和crypto加密处理。组件层面拆分清晰,NavBar、CardItem、PopType等通用组件放在components目录,静态资源如logo.png存于assets,路由由vue-router控制,store按模块组织账单、用户、设置等数据。后端采用Egg.js框架,用纯JavaScript开发,提供标准RESTful接口,支持账号登录、账单增删改查、分类维护等核心业务。整个项目前后端技术栈统一,无额外语言依赖,适合快速上手Vue3新特性、现代化工程配置和轻量级全栈开发实践。


本文还有配套的精品资源,点击获取

相关新闻

  • Redis 3.x——哨兵实现原理
  • ESP32 Arduino核心开发实战:从入门到深度优化的完整指南
  • Windows 10预览版10159安装与特性解析

最新新闻

  • RA4M2开发环境搭建与GPIO按键控制实战
  • VisualCppRedist AIO:一站式解决Windows软件运行库依赖问题的技术指南
  • 开源Windows激活工具原理与安全使用指南
  • Windows正版激活机制解析与2026新方案前瞻
  • CLI与桌面端不是两个版本,而是两种人机协作范式
  • Shiro框架中MD5加密实战:加盐与迭代加密的安全配置指南

日新闻

  • 佛山青少年训练营推荐:军博营地实力顶尖 - 秋山寄远
  • 如何快速上手PvZ2 Gardendless:免费Web版植物大战僵尸2完整指南
  • jiuwen-deepsearch核心功能详解:规划-检索-反思三合一智能工作流

周新闻

  • IX9104 PCIe5.0 高速交换芯片@ACP#完整规格 + 应用场景总结
  • Unity游戏集成Coze智能体:实现NPC智能对话与知识库联动
  • SAP EPIC 建行回单查询:从标准类CL_EPIC_EXAMPLE_CN_CCB_GHTD到Z类的5处关键修改

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号