ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于Node.js+uni-app+Vue的日常活动记录系统开发实践

基于Node.js+uni-app+Vue的日常活动记录系统开发实践 先说结论如果你想快速上线一个微信端的日常活动记录工具又不想在原生小程序开发里陷入重复造轮子的泥潭那么“Node.js uni-app Vue”这套组合是目前性价比极高的方案。它最大的好处是一套代码同时覆盖微信小程序、H5和App后端用Node.js统一提供接口前端业务逻辑几乎全部复用开发和维护成本直线下降。这篇文章不打算跟你谈太多理论而是从一套完整的“日常活动记录系统”项目出发把它拆开揉碎给你看包括项目架构思路、前后端核心代码怎么写、微信小程序端有哪些坑要绕以及那些你在官方文档里根本翻不到的经验细节。1. 项目定位与架构设计思路1.1 这套系统到底解决什么问题日常活动记录系统的核心需求其实很朴素用户能够快速记录每天发生的活动比如健身、阅读、饮食、工作安排并且能在事后按时间线查看、统计和回溯。听起来没什么特别的但真正做起来会发现需求远比想象的复杂——因为“活动”这个词本身是可扩展的不同用户对活动的定义不同记录字段也不同。如果一开始就把模型设计死后面每加一种活动类型就要改表结构迟早会把自己累垮。所以这个项目在设计之初就定了一个原则活动类型可配置记录字段可扩展。前端用uni-app动态渲染表单后端只存“JSON字段”这样既能满足“日常记录”这个核心场景又能为后续扩展预留空间。从技术栈选择上uniapp负责多端适配Vue负责前端交互Node.js负责接口服务和数据存储三者的角色分工非常明确。1.2 为什么选择Node.js uni-app Vue这组技术栈原生微信小程序不是不能用但你得接受两个现实一是它只能跑在微信里将来要做App或者H5还得重新写一套二是它的语法和组件模型虽然也在进步但开发体验和前端生态相比还是差了一截。用uni-app的核心原因就是看中它的多端编译能力和Vue开发体验Vue响应式的数据管理和组件化模式在实际开发中能明显提升编码效率特别是对于这类表单密集、交互场景多的应用。Node.js作为后端选型的原因也很简单——让前端同学用一套语言搞定全栈。Express或是Koa这样轻量级的框架足够支撑这类中低并发的数据管理系统而且生态里现成的验证、日志、数据库驱动库非常丰富不需要像Java那样配置一堆环境部署在云服务器上也很轻。选择这套技术栈最关键的一点是团队协作成本低。前端和后端共享部分类型定义接口文档约定好之后开发可以并行推进不会因为技术栈割裂导致互相等待。1.3 整体架构分层与模块划分整套系统从逻辑上分成三层客户端层uni-app Vue负责页面展示、用户交互、表单录入、数据缓存。服务端层Node.js Express负责鉴权、业务逻辑处理、数据持久化并把统一的JSON结构返回给前端。数据层MySQL存储用户数据、活动类型和记录详情。前端模块划分为首页看板、活动记录列表、新建/编辑记录表单、个人中心、数据分析页。后端模块划分为用户鉴权模块、活动类型管理模块、记录管理模块、统计汇总模块。以下是核心推荐目录结构├── client # uni-app工程目录 │ ├── pages │ │ ├── index # 首页/看板页 │ │ ├── record # 记录列表页 │ │ ├── edit # 新增/编辑记录页 │ │ ├── stats # 数据统计页 │ │ └── mine # 个人中心 │ ├── components # 公共组件 │ ├── api # 接口请求封装 │ ├── utils # 工具函数 │ └── static ├── server # Node.js后端工程目录 │ ├── routes # 路由层 │ ├── controllers # 控制器层 │ ├── services # 业务逻辑层 │ ├── models # 数据模型 │ ├── middlewares # 中间件 │ └── app.js2. 环境配置与踩坑实录2.1 Node.js安装与npm权限问题这套系统的开发环境依赖Node.js所以第一步就是装Node。Windows用户可以去官网下载LTS版本尽量别用最新版因为有些uni-app的依赖对最新版Node兼容性存疑LTS版本实测兼容性最好。安装时直接一路Next记住安装路径不要带中文和空格。很多人在装完Node之后遇到一个特别经典的问题在终端里执行npm命令时系统直接报错npm : 无法加载文件 D:\Program Files\nodejs\npm.ps1因为在此系统上禁止运行脚本。这个问题的根源是PowerShell的执行策略默认限制脚本运行最简单的解决办法有两种。第一种以管理员身份打开PowerShell执行Set-ExecutionPolicy -ExecutionPolicy RemoteSigned然后输入Y确认重新打开终端就正常了。第二种如果不想改全局策略也可以在终端里用cmd替代PowerShell或者在VSCode里把默认终端切到Command Prompt。这里有一个经验之谈改执行策略之后如果电脑上有多个版本的PowerShell或者有安全软件可能会被拦下来所以改完最好重启终端验证一下再继续。2.2 HBuilderX与微信开发者工具的配置uni-app项目我建议直接用HBuilderX来跑不是不能用命令行创建和编译而是HBuilderX对uni-app的语法提示、条件编译和模拟器支持做得更省心。下载HBuilderX之后还需要安装微信开发者工具然后在HBuilderX里配置微信开发者工具的安装路径否则点击“运行到小程序模拟器”时会找不到编译目标。具体操作菜单栏选择“工具” - “设置” - “运行配置”找到“微信开发者工具路径”把微信开发者工具的安装exe路径加进去。然后在微信开发者工具里需要打开“安全设置”并开启“服务端口”这样HBuilderX才能通过命令行自动唤起编译。这块配置看起来简单但是中间有一个很典型的坑HBuilderX和微信开发者工具的版本不兼容会导致编译成功后模拟器不刷新。我的解决办法是尽量把两边都更新到最新稳定版然后删除unpackage/dist/dev目录重新编译基本就能解决。2.3 Vue与uni-app版本组合建议这里说的Vue在uni-app里实际对应的是Vue 2或者Vue 3的语法模式。uni-app早期项目大多跑在Vue 2上组件库以uni-ui为主新版HBuilderX默认新建的uni-app项目已经支持Vue 3。如果你是非要从零开始的项目直接选Vue 3版本响应式性能更好组合式API写起来也顺手。不过有一个细节要提醒Vue 3的uni-app项目在部分低版本的小程序基础库上可能会有兼容问题所以微信开发者工具里要设置一个较新的调试基础库版本通常选3.0.0以上就比较安全。3. 系统核心模块设计与实现3.1 活动记录的数据模型设计日常活动记录系统的数据模型是整个项目的地基。我这边的设计是两张核心表加一张用户表users表字段类型说明idint主键自增openidvarchar(64)微信唯一标识nicknamevarchar(50)昵称avatarvarchar(255)头像地址created_atdatetime创建时间activity_types表字段类型说明idint主键自增user_idint所属用户type_namevarchar(50)活动类型名称iconvarchar(50)类型图标custom_fieldsjson自定义字段配置activity_records表字段类型说明idint主键自增user_idint所属用户type_idint活动类型IDcontentjson记录内容字段动态record_datedate记录日期created_atdatetime创建时间设计成这样的核心原因是content字段存JSON意味着用户给某个活动类型添加的任何自定义字段都可以直接序列化后存进去不需要为每一种活动单独建表。查询时虽然不能直接对JSON里的字段做SQL级筛选但配合JSON_CONTAINS函数也能实现基础过滤日常活动的数据量完全扛得住。3.2 Node.js后端接口开发要点后端用Express搭建关键接口如下POST /api/user/login微信登录换取openid查表有则直接返回用户信息没有则在users表插入新用户。GET /api/activity-types返回当前用户的所有活动类型。POST /api/activity-types新增活动类型。GET /api/records?date2024-01-01查询某天的活动记录。POST /api/records新增活动记录。PUT /api/records/:id修改记录。DELETE /api/records/:id删除记录。GET /api/stats/week近7天活动数据统计。写登录接口时最需要注意的是微信小程序wx.login拿到的code是一次性的后端要拿这个code去微信的接口换openid和session_key。用Node.js的axios请求微信接口就行const axios require(axios); async function code2Session(code) { const appid 你的appid; const secret 你的appsecret; const url https://api.weixin.qq.com/sns/jscode2session?appid${appid}secret${secret}js_code${code}grant_typeauthorization_code; const res await axios.get(url); return res.data; // 包含 openid 和 session_key }注意不要把appsecret写在前端代码里如果用的uni-app前端调用uni.login获得code后把code传给后端接口开房换openid的操作一律在Node.js端完成。3.3 接口安全与token鉴权微信小程序不像Web端那样天然适合用Cookie做会话管理这里我选的是token方案。用户登录成功后后端生成一个token可以用jwt返回给前端前端存到uni.setStorageSync(token, token)之后每次请求在header里带上// uni-app封装请求 const request (options) { return new Promise((resolve, reject) { const token uni.getStorageSync(token); uni.request({ url: BASE_URL options.url, method: options.method || GET, data: options.data || {}, header: { Content-Type: application/json, Authorization: Bearer ${token} }, success: (res) { if (res.statusCode 401) { // token失效跳转登录 uni.navigateTo({ url: /pages/login/login }); return; } resolve(res.data); }, fail: (err) reject(err) }); }); };后端用一个中间件统一解析Authorization头校验token的有效性再把当前用户ID挂到req.userId上。这样各个路由里可以直接从req.userId拿用户维度数据不用每次手动传用户ID。4. 前端核心页面开发详解4.1 首页看板的数据渲染逻辑首页是用户进入系统后看到的第一个页面主要展示今日记录概览、连续打卡天数和最新几条活动记录。从接口设计角度首页不需要单独写接口直接复用记录列表和统计接口的数据就够了。在页面加载时通过Promise.all同时发起两个请求async function loadDashboard() { const [recordRes, statsRes] await Promise.all([ getTodayRecords(), getWeekStats() ]); // 处理数据渲染 }一个是查询当日记录一个是查询近7天统计两边同时返回后一起渲染减少请求等待时间。页面侧的模板用Vue的列表渲染v-for输出活动记录卡片每条记录展示类型图标、标题、时间和备注。有一个体验优化的细节为了在弱网环境下也能快速看到内容我在前端给当天的记录做了一份缓存页面加载时优先显示缓存再把网络请求结果进行覆盖。4.2 动态表单的实现思路日常活动记录系统的核心痛点就在于动态表单。前端需要根据用户选中的活动类型动态渲染不同的字段。比如“健身”类型可能要记录“时长”和“消耗卡路里”“读书”类型要记录“页码”和“读后感”“饮食”类型要记录“食物名称”和“热量”。这里的解决办法是利用custom_fields配置。新增活动类型时用户可以配置若干个字段每个字段由label显示名、fieldName字段key、type文本/数字/日期等组成。前端拿到后动态渲染template v-forfield in currentType.custom_fields :keyfield.fieldName view classform-item text{{ field.label }}/text input v-iffield.type text v-modelformData[field.fieldName] placeholder请输入内容 / picker v-else-iffield.type date modedate changehandleDateChange input :valueformData[field.fieldName] / /picker input v-else-iffield.type number typedigit v-modelformData[field.fieldName] / /view /template提交时把整个formData作为content字段传给后端后端原样存入JSON字段。这个方案实现成本低但是灵活性极高任何新的活动类型都可以零成本扩展。4.3 记录列表的分页加载与下拉刷新记录列表页的数据量会随着时间增长不能一次性全量加载。我的方案是使用滚动触底加载下一页每次请求20条同时在页面提供日期筛选。实现思路是let page 1; const PAGE_SIZE 20; let hasMore true; async function loadRecords(reset false) { if (reset) { page 1; hasMore true; recordList.value []; } if (!hasMore) return; // 调用接口获取数据 const res await getRecords({ page, pageSize: PAGE_SIZE, date: selectedDate.value }); recordList.value recordList.value.concat(res.list); hasMore res.hasMore; page; }页面上通过onReachBottom事件监听滚动触底调用loadRecords(false)通过onPullDownRefresh来实现下拉刷新调用loadRecords(true)并结束loading动画。这里有个容易被忽略的小细节下拉刷新时需要调用uni.stopPullDownRefresh()手动关闭loading动画否则动画会一直转个不停看起来像卡死了一样。5. 微信小程序端特殊适配与问题排查5.1 微信小程序顶部导航栏高度适配日常活动记录系统的首页如果要做一个沉浸式header效果会遇到小程序胶囊按钮遮挡内容的问题。不同机型和微信版本胶囊按钮的位置和状态栏高度都不一样不能写死CSS。适配的方法是动态获取系统信息const systemInfo uni.getSystemInfoSync(); // 状态栏高度 const statusBarHeight systemInfo.statusBarHeight; // 胶囊按钮信息仅微信小程序支持 const menuButtonInfo uni.getMenuButtonBoundingClientRect(); // 导航栏高度 (胶囊顶部 - 状态栏高度) * 2 胶囊高度 const navBarHeight (menuButtonInfo.top - statusBarHeight) * 2 menuButtonInfo.height;拿到这两个值之后动态设置页面的内边距就能保证内容不会被胶囊按钮挡住。这块在H5端要做一个条件编译处理因为H5没有胶囊按键的概念直接用固定值就行。5.2 微信小程序静音状态下播放视频的问题如果在记录详情里上传了视频附件小程序在某些场景下会出现iOS静音模式下播放视频没有声音的问题。这个其实是微信小程序的原生限制iOS在静音模式下默认视频播放是不出声的。解决办法是在video组件上设置playSilently属性或者通过uni.createVideoContext拿到上下文之后手动设置const videoContext uni.createVideoContext(myVideo); videoContext.play();如果是要做更复杂的音频播放场景还需要关注wx.setInnerAudioOption的配置wx.setInnerAudioOption({ obeyMuteSwitch: false, // 不遵循静音开关保证有声 success: () {} })这个配置能让iOS设备在静音状态下也能正常播放音频对记录类应用中包含语音备注的场景很实用。5.3 H5与小程序的多域名指向问题使用uni-app开发完后如果要打包H5部署可能会遇到一个需求一个打包产物需要指向不同的后端域名比如测试环境一个域名生产环境一个域名。官方推荐的方案是使用环境变量在manifest.json的h5配置里设置不同的devServer.proxy但实际项目里更灵活的做法是运行时不写死域名而是放到一个可配置的地方// utils/config.js const ENV production; // 切换这里即可 const CONFIG { development: { BASE_URL: http://localhost:3000 }, production: { BASE_URL: https://api.yourapp.com } }; module.exports CONFIG[ENV];但如果是H5要动态指向两个域名还可以在index.html里通过注入全局变量实现或者通过location.hostname判断当前域名来决定请求哪个后端地址。这种方式适合需要把同一个H5部署在两个不同站点、对接不同后端服务的场景。5.4 微信小程序扫码功能的集成如果你需要在记录系统的“签到/打卡”场景里使用扫一扫可以直接调微信小程序的扫码API不需要引入额外插件uni.scanCode({ scanType: [barCode, qrCode], success: (res) { const result res.result; // 处理扫码结果比如识别某个场馆ID、某个活动的编号 handleScanResult(result); }, fail: (err) { console.log(扫码失败, err); } });这个API直接支持在小程序内调用但对个人主体的小程序有类目限制如果类目不匹配真机上调用时会报错。开发前先去微信公众平台确认一下自己小程序的类目是否支持扫码能力。6. 编译打包与上线发布流程6.1 uni-app项目打包微信小程序的详细步骤开发完成后要打包发布在原生的微信开发者工具里操作其实很繁琐但uni-app把它简化了。操作步骤如下在HBuilderX中打开项目点击菜单栏“发行” - “小程序-微信”。填写小程序AppID如果没有测试号就选“不使用AppID”。点击“发行”按钮HBuilderX会自动编译代码并生成unpackage/dist/dev/mp-weixin目录。打开微信开发者工具选择“导入项目”目录选到上面那个mp-weixin文件夹。在开发者工具里预览、调试确认无误后再点击“上传”按钮上传代码。到微信公众平台后台进入“版本管理”将上传的版本提交审核。有个很常见的坑是上传后微信提示“代码包大小超过限制”。uni-app项目编译后如果使用了较大的图片或组件库包体积容易超标。解决办法有几种在manifest.json里开启“代码分包”把非首页的页面放到subPackages里。压缩图片资源能不放在static里的尽量走CDN按需引入组件不要一次性全量引入UI库6.2 发布安卓应用市场时的注意事项如果后续要把这套系统打包成安卓App在uni-app里通过“发行” - “原生App-云端打包”就能生成安装包。不过在上架安卓市场时需要注意几个细节首先需要准备好软著证书或者App备案信息各大应用市场都有要求其次某些应用市场对App的权限声明查得很严格比如如果申请了位置权限就必须在隐私政策里说清楚用途。我推荐在manifest.json里把不需要的权限模块全部去掉只保留必要的网络、存储、相机权限。很多开发者嫌麻烦留着默认权限配置结果审核被拒白等好几天完全没必要。6.3 Node.js后端的部署方案后端Node.js项目部署最简单的方式是用PM2守护进程。在服务器上安装好Node.js环境后把项目上传到服务器执行npm install --production pm2 start app.js --name activity-server然后用Nginx做反向代理把域名对应的请求转发到Node.js的端口上同时处理HTTPS证书。还需要在微信公众平台后台把服务器域名加到“开发设置”里的“服务器域名”白名单否则小程序请求会被拦截。特别注意在小程序真机调试时所有请求的接口域名必须是HTTPS且已备案否则会直接报request:fail错误。本地开发时可以在开发者工具里勾选“不校验合法域名”但真机预览该选项不生效。7. 项目优化与常见实用技巧7.1 前端缓存策略与性能优化日常活动记录系统的用户会频繁打开首页查看今日记录和统计数据如果每次打开都重新请求体验会受到影响。我实际采用的是三步策略第一今日记录缓存到uni.setStorageSync页面加载立即显示缓存内容。第二网络请求成功后对比数据是否变化有变化再更新页面和缓存。第三统计结果做增量更新比如每次新增记录后只更新本地统计缓存不必每次都拉取全量统计。这样处理后首页加载速度明显提升弱网环境下也不会白屏。7.2 防止录屏与内容保护如果是商业应用可能担心用户录屏泄露信息。微信小程序里没有一个标准的“禁止录屏”API但可以用wx.setVisualEffectOnCapture来设置截屏/录屏时隐藏页面内容wx.setVisualEffectOnCapture({ visualEffect: hidden })这个API只在安卓端部分机型有效iOS端支持一般。真正要保护敏感数据还得靠后端接口限制返回字段别把用不到的数据发给前端。前端防录屏只能算一道心理防线不能作为唯一的安全手段。7.3 数据统计与可视化的实现思路日常活动记录系统的延伸价值在于帮用户形成数据洞察。我的统计页面用了简单的图表库比如qiun-data-charts它兼容uni-app可以直接在微信小程序里渲染折线图和柱状图。统计页面展示三个维度近7天记录次数趋势折线图活动类型占比饼图每日平均记录时长柱状图后端统计接口直接SQL聚合SELECT DATE(record_date) as date, COUNT(*) as count FROM activity_records WHERE user_id ? AND record_date DATE_SUB(CURDATE(), INTERVAL 7 DAY) GROUP BY DATE(record_date)前端拿到数组后直接传给图表组件非常直接。8. 常见问题排查与避坑手册8.1 前端问题速查表问题现象可能原因解决办法编译后小程序白屏Vue版本与小程序基础库不兼容升级基础库版本或检查console报错请求接口一直失败域名未加白名单到微信公众平台配置服务器域名图片加载不出来图片域名没配置在小程序后台downloadFile合法域名里加上分享好友链接打不开没配置分享路径onShareAppMessage返回正确path视频在iOS静音没声音iOS默认静音模式设置playSilently或obeyMuteSwitch: false8.2 后端问题速查表问题现象可能原因解决办法code2Session返回40029code重复使用或已过期确保每次登录用新拿到的codetoken过期后接口报401jwt过期且无刷新机制前端捕获401跳转重新登录接口跨域报错域名或端口不匹配后端开CORS或Nginx配置proxy_pass数据库连接超时连接池设置太小调大连接池上限connectionLimit8.3 实际项目中最容易忽略的几个细节第一个是时间字段记录系统涉及日期统计千万不要在前端把日期格式化成字符串传给后端最好直接传时间戳或ISO字符串后端统一处理时区否则不同手机时区差异会导致统计数据错乱。第二个是页面卸载时清理定时器如果在页面里用了setInterval做倒计时或自动刷新页面销毁时一定要clearInterval否则页面栈存在导致定时器持续运行耗电同时还会造成性能问题。第三个是并发重复提交用户连续点击保存按钮会触发两条重复记录前端要加“提交中”状态锁后端也最好做一个短时间内的幂等判断双保险更稳妥。第四个是微信开发者工具和HBuilderX的缓存问题有的时候代码修改了但真机预览还是旧效果。别慌关掉微信开发者工具清理unpackage/dist目录HBuilderX重新编译运行大概率能解决。9. 项目扩展方向思考日常活动记录系统做成之后可以自然扩展的方向其实不少。比较实用的是把“活动”和“日历”结合起来做成按日期维度展示的日历视图。用户可以在日历上直接看到每天的记录密集程度点某一天展开详情。这个交互对生活管理类应用是刚需技术实现也不难uni-calendar组件或自己用picker模式为date实现都行。另一个方向是加入数据导出功能。虽然小程序的体验限制比较多但可以引导用户到H5端下载导出文件后端生成Excel或CSV之后提供下载链接满足用户备份数据、二次分析的需求。还有一个我很看好的方向是目标管理。在记录的基础上设定目标比如“每周跑步3次”“每天阅读30分钟”系统自动对比记录数据和目标值的差距在首页展示完成进度。这个功能上线后用户的粘性提升非常明显因为它把单纯的记录工具变成了一个有反馈的激励工具。10. 总结与个人实操感触带这个项目走完整条链路之后我最大的体会是选型只是开始真正花时间的地方永远在细节里。uni-app的确能帮我们省去多端适配的大量工作但它的坑也很多条件编译、生命周期差异、不同端组件表现不一致这些问题几乎不可能只靠读文档避免必须实测。Node.js后端本身不复杂但鉴权、校验、异常处理、部署上线这一套流程每一步都需要认真对待。我个人在做记录类项目时会习惯先把数据模型想清楚再动手写代码。这次用的JSON字段方案虽然灵活但查询和统计效率有上限如果将来记录量非常大还是要考虑拆表或者引入文档型数据库。取舍之间轻量级系统怎么选就看你最看重什么。最后再分享一个小技巧微信小程序真机调试时出现“接口报错但看不出来具体原因”的情况不要只盯着Network面板可以在代码里加一些日志上报把请求参数和返回结果传到一个远端日志服务上排查问题的效率会高非常多。这套系统上线后我也一直在维护后续有机会再把数据统计模块的深入实现拿出来单独聊聊。
返回列表