尧图精选

uniapp旅游小程序实战:协同过滤推荐与Echarts地图集成

🕒 发布时间:2026/9/8 4:09:35 📁 来源:尧图网络
如果你最近在做一个旅游类小程序而且正好选了uniapp这套跨端框架那这个项目可以拿来直接参考。它的核心不只是“小程序能展示景点列表”而是把三类技术揉在了一起协同过滤算法做个性化推荐、Echarts做数据可视化分析、腾讯地图 API做定位与景点展示。这三块恰好是旅游类小程序里面比较有代表性的功能组合也是面试和毕业设计中容易挂出来的亮点。整篇文章会按照实际项目的开发顺序来走先说清楚功能模块和核心技术栈再讲环境准备、数据模型、算法落地、图表绘制、地图接入最后给出常见报错和排查思路。无论你是学生做课程设计还是前端开发想在小程序里集成算法与地图能力都可以照着这套思路去复现。1. 项目核心能力速览能力项说明项目类型基于 uniapp 的旅游类微信小程序支持跨端编译核心算法协同过滤推荐包含用户相似度计算与景点推荐可视化方案Echarts 图表展示景点热度、游客来源、路线分布地图能力腾讯地图 API支持定位、景点坐标展示、POI 搜索前端框架uniapp Vue 语法可发布到微信小程序、H5 等多端数据存储本地 mock 数据 / 后端 API 接口均可适配推荐功能根据用户的历史行为或评分数据生成个性化景点推荐列表适合人群前端学习者、毕业设计开发者、小程序实战项目入门者这个项目比较推荐的做法是先把界面和数据层跑通再逐步加入算法和图表模块避免一上来就把全部逻辑堆到同一个页面里。2. 系统功能架构与模块划分一个完整的旅游小程序不能只有景点列表需要围绕“发现景点 - 查看详情 - 推荐匹配 - 数据统计”这条主线来设计页面结构。2.1 页面模块从材料里的功能和常见旅游类小程序来看页面结构可以拆成五个核心模块。首页模块顶部搜索框支持按城市或景点名称搜索。Banner 轮播图展示热门目的地。分类入口比如“自然风光”“人文古迹”“主题乐园”等。推荐景点列表优先展示协同过滤算出的个性化结果。景点列表与详情模块景点卡片展示封面图、名称、评分、标签、距离。详情页包含介绍、开放时间、门票信息、用户评价。详情页内嵌入腾讯地图展示景点坐标并支持导航跳转。个性化推荐模块新用户默认展示热门景点。老用户根据历史收藏和评分生成推荐列表。推荐结果字段包含“推荐指数”和“推荐理由”。数据统计与分析模块当前用户的浏览行为统计。全局景点热度排行。游客来源分布、月份出行趋势、景点分类占比。个人中心模块用户登录状态。我的收藏、我的足迹、我的评分。设置与隐私授权管理。2.2 数据层结构数据层在项目初期建议先使用本地mock数据保证算法和图表能跑通再替换成后端 API。数据模型主要包含数据表字段建议作用用户表id, username, city, travel_pref用户基础信息与偏好景点表id, name, city, category, score, lat, lng, cover景点基础数据与坐标评分表user_id, scenic_id, rating协同过滤推荐的核心数据收藏表user_id, scenic_id, create_time行为数据补充浏览记录表user_id, scenic_id, duration用户行为分析推荐算法的数据来源主要是评分表和收藏表所以这两张表在 mock 阶段就要把数据填充到一定数量否则算不出有区分度的结果。3. 环境准备与项目初始化3.1 开发工具HBuilderXuniapp 官方推荐 IDE支持直接运行到微信开发者工具。微信开发者工具用于预览、调试、上传微信小程序。Node.js用于安装 npm 依赖比如 Echarts 相关包。HBuilderX 创建项目的路径是文件 - 新建 - 项目 - 选择uni-app模板。语言选择 Vue 3 或 Vue 2 都可以目前 uniapp 对 Vue 3 的支持已经很成熟建议直接用 Vue 3 版本。3.2 项目配置创建项目后需要先检查manifest.json确认微信小程序相关的 appid 配置。如果还没有小程序 appid可以在微信公众平台注册测试号也可以在 HBuilderX 里先用测试 appid 运行。基础配置示例{ name: 旅游小程序, appid: , description: 基于uniapp的旅游推荐与多维数据分析小程序, versionName: 1.0.0, versionCode: 100, mp-weixin: { appid: 你的小程序appid, setting: { urlCheck: false, es6: true, minified: true }, usingComponents: true } }3.3 安装依赖在项目根目录执行npm init -y npm install dcloudio/uni-ui npm install echarts如果是用 HBuilderX 直接创建的项目也可以不通过 npm而是直接使用uni_modules插件市场里的 echarts 组件这个方式对新手更友好不需要手动配置构建工具。4. 协同过滤算法在旅游推荐中的落地4.1 为什么用协同过滤旅游推荐和电商推荐不太一样用户的兴趣维度很分散。有人喜欢自然风光有人偏好文化古迹有人只看亲子乐园。基于内容的推荐需要给景点打大量标签维护成本高协同过滤只需要基于用户对景点的评分或行为数据就能算出“和你相似的人喜欢什么”然后把这些景点推荐给你。4.2 算法流程基于用户的协同过滤核心流程是构建用户-景点评分矩阵。计算目标用户和其他用户之间的相似度。选取 Top-N 相似用户。根据相似用户的评分加权预测目标用户对未接触景点的评分。取评分最高的若干个景点作为推荐结果。4.3 核心代码实现在 uniapp 项目中算法模块可以放在utils/recommend.js里通过输入评分数据和目标用户 ID输出推荐景点列表。// 基于用户的协同过滤推荐算法 function computeUserSimilarity(userRatings) { const similarityMatrix {}; const userIds Object.keys(userRatings); userIds.forEach((userId) { similarityMatrix[userId] {}; userIds.forEach((otherUserId) { if (userId otherUserId) return; const commonItems Object.keys(userRatings[userId]).filter( (itemId) userRatings[otherUserId][itemId] ); if (commonItems.length 0) { similarityMatrix[userId][otherUserId] 0; return; } let dotProduct 0; let normA 0; let normB 0; commonItems.forEach((itemId) { const ratingA userRatings[userId][itemId]; const ratingB userRatings[otherUserId][itemId]; dotProduct ratingA * ratingB; normA ratingA * ratingA; normB ratingB * ratingB; }); similarityMatrix[userId][otherUserId] dotProduct / (Math.sqrt(normA) * Math.sqrt(normB)); }); }); return similarityMatrix; } function recommendScenic(userId, userRatings, allScenicIds, topN 5) { const similarityMatrix computeUserSimilarity(userRatings); const targetUserRatings userRatings[userId] || {}; const scores {}; allScenicIds.forEach((scenicId) { if (targetUserRatings[scenicId]) return; let totalScore 0; let totalSim 0; Object.keys(similarityMatrix[userId]).forEach((otherUserId) { const sim similarityMatrix[userId][otherUserId]; const otherRating userRatings[otherUserId][scenicId]; if (sim 0 otherRating) { totalScore sim * otherRating; totalSim sim; } }); if (totalSim 0) { scores[scenicId] totalScore / totalSim; } }); return Object.entries(scores) .sort((a, b) b[1] - a[1]) .slice(0, topN) .map(([scenicId, score]) ({ scenicId, score: score.toFixed(2) })); } module.exports { recommendScenic, computeUserSimilarity };这段代码的核心点在于先构建用户相似度矩阵再对目标用户没去过且没评分的景点进行加权评分预测。实际项目中用户评分数据如果放在后端那么算法最好也放在 Node.js 服务端执行小程序端只负责传用户 ID 和接收推荐结果。4.4 推荐测试用例测试推荐算法时可以用一组简化的 mock 数据验证const userRatings { userA: { scenic1: 5, scenic2: 3, scenic3: 4 }, userB: { scenic1: 4, scenic3: 5, scenic4: 2 }, userC: { scenic2: 5, scenic4: 4, scenic5: 3 }, }; const allScenicIds [scenic1, scenic2, scenic3, scenic4, scenic5]; // 为 userA 推荐 const result recommendScenic(userA, userRatings, allScenicIds); console.log(result);判断推荐是否合理的标准是userB 和 userA 都高分评价了 scenic1 和 scenic3所以scenic4应该出现在 userA 的推荐列表中。如果结果里出现这个逻辑说明算法链路没有问题。5. Echarts 图形化分析模块5.1 在 uniapp 中引入 EchartsEcharts 在 uniapp 中不能直接当作普通组件使用有几种接入方式使用uni_modules里的qiun-data-charts封装组件底层支持 Echarts 和 ucharts。使用lime-echart组件通过 renderjs 在 webview 中渲染 Echarts。直接通过renderjs手动集成 Echarts。推荐使用qiun-data-charts或lime-echart两者的文档都比较完整兼容微信小程序。5.2 景点热度分析饼图饼图适合展示景点分类占比比如“自然风光 40%”“人文古迹 35%”“主题乐园 25%”。Echarts 配置示例export function getCategoryPieOption(data) { return { tooltip: { trigger: item, }, legend: { bottom: 0%, }, series: [ { name: 景点分类占比, type: pie, radius: [40%, 70%], avoidLabelOverlap: false, itemStyle: { borderRadius: 6, borderColor: #fff, borderWidth: 2, }, label: { show: true, formatter: {b}: {d}%, }, data: data, }, ], }; }5.3 游客来源分析与月度趋势游客来源分布适合用中国地图展示月度出行趋势适合用折线图。中国地图需要引入地图 JSON 数据Echarts 官方从 5.x 版本开始不再内置地图数据需要单独加载。折线图配置示例export function getTrendLineOption(months, counts) { return { tooltip: { trigger: axis, }, xAxis: { type: category, data: months, }, yAxis: { type: value, name: 出行人数, }, series: [ { name: 月度出行趋势, type: line, smooth: true, data: counts, areaStyle: { color: rgba(56, 132, 255, 0.15), }, }, ], }; }5.4 图表数据来源图表的底层数据需要和后端统计接口对齐。比如“景点热度排名”柱状图数据字段至少包含scenicName和viewCount“游客来源”地图需要province和userCount字段。如果项目处于纯前端阶段可以提前设计好统计接口返回的数据结构再用 mock 数据填充图表后续联调时只需要替换数据源不改图表逻辑。6. 腾讯地图 API 接入与定位6.1 申请腾讯地图 Key使用腾讯地图 API需要先在腾讯位置服务官网注册账号创建应用后获取 Key。小程序端使用地图组件不强制要求 Key但获取用户定位、逆地址解析、POI 检索这些能力必须在腾讯位置服务控制台开通。申请时建议开启微信小程序授权并绑定小程序的 AppID。这样在微信开发者工具里调试时权限校验会顺畅很多。6.2 在 uniapp 中获取定位uniapp 获取定位的通用方式是uni.getLocation。在微信小程序端需要先在manifest.json的mp-weixin节点配置permission字段否则用户授权时会直接失败。{ mp-weixin: { permission: { scope.userLocation: { desc: 你的位置信息将用于获取附近景点 } } } }获取定位代码uni.getLocation({ type: gcj02, success(res) { console.log(当前纬度 res.latitude); console.log(当前经度 res.longitude); // 拿到坐标后可以传给地图组件或者用于 POI 搜索 }, fail(err) { console.error(定位失败, err); }, });6.3 地图组件展示景点uniapp 内置了map组件可以直接通过经纬度渲染景点位置。一组景点坐标可以渲染多个marker。template map :latitudelatitude :longitudelongitude :markersmarkers :scalescale classmap-container / /template数据绑定export default { data() { return { latitude: 39.9042, longitude: 116.4074, scale: 10, markers: [ { id: 1, latitude: 39.9163, longitude: 116.3972, title: 故宫博物院, iconPath: /static/icon-location.png, width: 30, height: 30, }, { id: 2, latitude: 39.9847, longitude: 116.3184, title: 颐和园, iconPath: /static/icon-location.png, width: 30, height: 30, }, ], }; }, };6.4 逆地址解析与周边搜索小程序端直接调用腾讯地图 WebService API 需要使用request域名白名单开发阶段可以在微信开发者工具中关闭合法域名校验来调试。逆地址解析核心参数参数名是否必填说明location是纬度,经度key是腾讯位置服务 Keyget_poi否是否返回周边 POI1 返回请求示例https://apis.map.qq.com/ws/geocoder/v1/?location39.9163,116.3972keyYOUR_KEYget_poi1返回结果中的result.pois就是当前坐标附近的兴趣点可用于“附近的景点推荐”功能。7. 接口 API 与批量数据加载设计7.1 接口返回结构约定不管数据来自 mock 还是后端服务建议统一接口返回格式方便前端通用处理。{ code: 0, message: success, data: { total: 100, list: [ { id: 1, name: 故宫博物院, city: 北京, category: 人文古迹, score: 4.9, lat: 39.9163, lng: 116.3972, cover: https://example.com/cover.jpg } ] } }code字段 0 表示成功非 0 表示业务异常。data.list是核心数据数组。7.2 uniapp 请求封装统一封装request方法后续所有页面都通过这个方法加载数据方便在 Header 里带上 token。const BASE_URL https://your-api-server.com/api; export function request(path, method GET, data {}) { return new Promise((resolve, reject) { uni.request({ url: BASE_URL path, method, data, header: { Content-Type: application/json, Authorization: uni.getStorageSync(token) || , }, success(res) { if (res.data.code 0) { resolve(res.data.data); } else { uni.showToast({ title: res.data.message, icon: none }); reject(res.data); } }, fail(err) { reject(err); }, }); }); }7.3 批量数据加载与分页旅游小程序里景点列表数据量会很大不建议一次性加载全部数据。推荐分页加载每页 10 到 20 条用户上拉触底时加载下一页。列表页分页逻辑export default { data() { return { scenicList: [], page: 1, pageSize: 10, hasMore: true, loading: false, }; }, methods: { async loadScenicList(reset false) { if (this.loading) return; if (reset) { this.page 1; this.hasMore true; this.scenicList []; } if (!this.hasMore) return; this.loading true; try { const data await request( /scenic/list?page${this.page}pageSize${this.pageSize} ); this.scenicList this.scenicList.concat(data.list); this.hasMore this.scenicList.length data.total; this.page 1; } finally { this.loading false; } }, }, onReachBottom() { this.loadScenicList(); }, };批量加载时还要注意图片资源加载策略。列表页如果一次渲染太多图片建议使用image组件的lazy-load属性避免小程序端出现卡顿。8. 资源占用与性能观察8.1 启动阶段小程序包体积直接影响首次加载速度。uniapp 项目编译到微信小程序后主包不能超过 2MB否则无法上传。Echarts 本身体积较大建议放在分包中或者在需要图表分析的页面再动态加载。8.2 列表渲染性能景点列表页和推荐结果页容易遇到长列表问题。列表超过 50 条时可以考虑使用recycle-list或分页加载来优化渲染。数据量比较小的项目直接v-for渲染也没有太大问题。8.3 图表渲染与内存Echarts 图表在页面销毁时必须调用dispose释放实例否则页面反复切换会积压内存占用导致小程序卡顿。onUnload() { if (this.chart) { this.chart.dispose(); this.chart null; } }如果使用lime-echart组件生命周期内部通常已经做了清理但自定义初始化多个图表实例时仍然要手动释放。9. 常见问题与排查方法问题现象可能原因排查方式解决方案运行到微信开发者工具没反应HBuilderX 与微信工具端口未配对查看微信开发者工具设置中的安全设置开启服务端口HBuilderX 中重新运行Echarts 图表不显示renderjs 兼容性问题或图表容器高度为 0检查图表容器 CSS 高度和初始化日志给图表容器设置固定高度检查组件引入方式定位失败缺少 permission 配置或用户拒绝授权在 manifest.json 中检查 location 权限补充scope.userLocation配置调用uni.openSetting引导重开授权地图 marker 不显示marker 的 iconPath 路径错误查看控制台图片加载日志把图标放到 static 目录使用绝对路径引用协同过滤推荐结果为空目标用户没有行为数据或相似度计算全为 0打印相似度矩阵和评分数据检查新用户补充热门兜底推荐逻辑接口请求失败微信小程序域名白名单未配置查看开发者工具 network 面板开发期关闭域名校验上线前配置合法域名小程序打包后包体积超限Echarts 等依赖库占体积过大查看详情包分析使用分包加载图表页面放在独立分包上拉触底重复加载没有加 loading 状态锁检查 onReachBottom 调用次数在请求前后设置 loading 标志位9.1 新增页面后主页无变化如果新建页面后首页看不到入口检查pages.json的页面注册列表。uniapp 中每个页面都要在pages.json中注册而且第一个页面就是小程序的启动页。9.2 用户在隐私政策弹窗点不同意微信小程序上架后要求符合隐私保护指引。如果用户拒绝隐私政策后仍然停留在主页面可能被审核驳回。正确做法是在用户拒绝后退出页面或者停留在“仅浏览首页”状态不请求任何隐私接口。10. 最佳实践与合规提醒推荐算法生成的结果需要标注“根据您的浏览偏好推荐”不要伪装成官方排名。用户位置信息属于敏感信息获取前必须有明确的目的说明不能静默调用uni.getLocation。景点图片和介绍存在版权风险尤其是毕业设计或商用项目尽量使用自有拍摄图片或可商用图库。用户评分、收藏、浏览记录属于行为数据存储和后端接口传输时要脱敏不能泄露用户身份信息。如果项目要发布到微信小程序正式环境后端接口必须全部配置为 HTTPS 合法域名。腾讯地图 API 的 Key 不要直接暴露在代码仓库里建议通过后端代理转发请求由服务端统一签发和鉴权。协同过滤算法在用户量小的时候效果不明显建议先用热门推荐兜底再根据行为数据逐步切换到个性化推荐。从开发角度这个项目最值得优先验证的是协同过滤算法的推荐链路前端传入用户 ID后端返回推荐列表中间不经过人工筛选。只要这条链路能跑通整个项目的技术结构就立住了。最容易踩的坑反而不是算法而是 Echarts 在微信小程序里的渲染兼容性和地图定位的权限配置这两块如果在开发初期不先试点后期容易反复返工。做完基础版本之后还可以继续扩展的方向包括接入后端用户登录态、推荐结果增加理由解释、图表数据接入实时统计接口、使用 WebSocket 做用户行为实时上报。对于课程设计或简历项目来说能够做到“算法可解释 数据可视化 地图联动”这三个点已经足够完整了。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →