当前位置: 首页 > news >正文

第十一章 app.js 全局状态与 openid 获取

📚 系列教程:微信小程序投票系统完整开发
🔗 上一章:第十章 - 结果页 result 开发
🔗 下一章:第十二章 - 后端接口设计与数据库建表


11.1 为什么 openid 如此重要

openid 是微信对用户在某个特定小程序下的唯一标识,具有以下特性:

特性说明
唯一性同一个用户在同一个小程序的 openid 永远不变
隔离性同一个用户在不同小程序的 openid 不同
安全性不暴露用户真实微信号,隐私友好
用途识别用户身份、防重复投票、关联用户数据

11.2 openid 获取的完整时序图

小程序端 后端服务 微信服务器 │ │ │ │── 1. wx.login() ───────►│ │ │◄─ 2. 返回临时 code ──────│ │ │ │ │ │── 3. POST /wx/user/login │ │ │ { code: "xxx" } ──►│ │ │ │── 4. GET code2Session ──►│ │ │ appid+secret+code │ │ │◄─ 5. { openid, session } │ │ │ │ │◄─ 6. { code:200, │ │ │ data: openid } ──│ │ │ │ │ │─ 7. 缓存到 Storage ──────┤ │ │─ 8. 通知所有等待回调 ──────┤ │

关键点

  • code有效期5分钟,且只能使用一次
  • AppSecret只能在后端使用,绝不能放在小程序代码中
  • 后端调用微信 API 的地址:https://api.weixin.qq.com/sns/jscode2session

11.3 当前 app.js 完整代码解析

// app.jsApp({globalData:{userInfo:null,openid:'',baseUrl:'https://www.chinahanwucun.cn',_openidReady:false,// openid 是否已就绪_openidCallbacks:[]// 等待 openid 的回调队列},/** * 对外暴露的 openid 获取方法 * 如果 openid 已就绪 → 立即回调 * 如果未就绪 → 加入队列,就绪后统一回调 * * 使用方式(在页面 js 中): * const app = getApp() * app.getOpenid(openid => { ... }) */getOpenid(callback){if(this.globalData._openidReady){callback(this.globalData.openid)}else{this.globalData._openidCallbacks.push(callback)}},/** * 内部方法:openid 就绪,通知所有等待的回调 */_resolveOpenid(openid){this.globalData.openid=openidthis.globalData._openidReady=true// 清空队列,依次执行所有等待的回调constcbs=this.globalData._openidCallbacksthis.globalData._openidCallbacks=[]cbs.forEach(cb=>cb(openid))},onLaunch(){// 步骤1:优先读取缓存,让页面请求能立即发出(不用等待登录完成)constcached=wx.getStorageSync('openid')if(cached){this._resolveOpenid(cached)}// 步骤2:每次启动都重新登录(刷新 session,微信 session 有效期约7天)wx.login({success:res=>{if(res.code){wx.request({url:`${this.globalData.baseUrl}/wx/user/login`,method:'POST',header:{'content-type':'application/json'},data:{code:res.code},success:r=>{if(r.data&&r.data.data){constopenid=r.data.data wx.setStorageSync('openid',openid)// 更新缓存// 如果之前没有缓存(首次使用),现在通知等待的回调if(!cached){this._resolveOpenid(openid)}else{// 已经通知过了,只更新全局变量即可this.globalData.openid=openid}}elseif(!cached){this._fallbackOpenid()}},fail:()=>{if(!cached)this._fallbackOpenid()}})}},fail:()=>{if(!cached)this._fallbackOpenid()}})},/** * 降级策略:当网络异常或接口异常时,生成一个临时 ID * 用 'tmp_' 前缀区分,后端可做相应处理 */_fallbackOpenid(){letopenid=wx.getStorageSync('openid')if(!openid){openid='tmp_'+Date.now()+'_'+Math.random().toString(36).substr(2,8)wx.setStorageSync('openid',openid)}this._resolveOpenid(openid)}})

11.4 回调队列机制详解

这是一个经典的异步初始化 + 等待队列模式:

// 场景:页面 A 和页面 B 同时加载,都需要 openid// 页面 A onLoad(openid 还没好)app.getOpenid(openid=>{// 此回调被推入 _openidCallbacks 队列fetchVoteList(openid)})// 页面 B onLoad(openid 还没好)app.getOpenid(openid=>{// 此回调也被推入 _openidCallbacks 队列fetchUserInfo(openid)})// 此时 _openidCallbacks = [fetchVoteList回调, fetchUserInfo回调]// ... 等待 wx.login + 后端接口 ...// openid 获取成功,_resolveOpenid 被调用// → 依次执行队列中所有回调// → fetchVoteList 和 fetchUserInfo 同时收到 openid 并执行

11.5 常见错误与解决方案

错误1:直接读取 globalData(时序问题)

// ❌ 错误:app.js 还在异步获取 openid,这里可能拿到空字符串onLoad(){constopenid=app.globalData.openid// 可能是 ''wx.request({data:{openid}})// 请求带了空 openid}// ✅ 正确:使用回调,确保 openid 就绪后再发请求onLoad(){app.getOpenid(openid=>{wx.request({data:{openid}})// openid 一定有值})}

错误2:code 被多次使用

// ❌ 错误:每次需要 openid 都调用 wx.login// wx.login 返回的 code 只能用一次,多次调用会导致之前的 code 失效// ✅ 正确:只在 app.js onLaunch 中调用一次 wx.login// 后续通过 getOpenid() 方法复用已获取的 openid

错误3:AppSecret 放在前端

// ❌ 严重错误:小程序代码可以被反编译,AppSecret 会泄露!wx.request({url:'https://api.weixin.qq.com/sns/jscode2session',data:{appid:'xxx',secret:'AppSecret泄露了!!!',js_code:code}})// ✅ 正确:code 发给自己的后端,由后端持有 AppSecret 去换 openidwx.request({url:'https://your-server.com/wx/user/login',data:{code:code}// AppSecret 在后端的 application.properties 中配置})

11.6 全局状态管理扩展

除了 openid,app.js 还可以管理其他全局状态:

App({globalData:{openid:'',baseUrl:'https://www.chinahanwucun.cn',// 扩展:用户信息缓存userInfo:null,// 扩展:全局配置(从后端拉取)config:{maxOptionsPerVote:10,voteTitleMaxLength:50}},// 扩展:检查网络状态checkNetwork(callback){wx.getNetworkType({success(res){if(res.networkType==='none'){wx.showToast({title:'当前无网络连接',icon:'none'})callback(false)}else{callback(true)}}})}})

本章小结

✅ 深入理解了 openid 的唯一性和获取时序
✅ 完全理解了回调队列机制(异步初始化模式)
✅ 掌握了正确使用getOpenid()的方式
✅ 知道了 3 个典型错误场景及避坑方法

下一章:后端接口设计与完整的数据库建表语句。


http://www.zskr.cn/news/1370715.html

相关文章:

  • ThinkPHP 5.x远程代码执行漏洞(CVE-2018-1002015)深度解析与实战防御
  • py每日spider案例之某tina天ji金数据采集脚本
  • 【无标题】平战一体双重防护,无感定位全天候守护井下作业安全 ——镜像视界井下“常态防控+灾变应急”双模式安全体系
  • 2026年OpenClaw如何部署?阿里云部署及配置Token Plan保姆级教程
  • InTec框架:三层协同边缘AI架构的设计原理与工程实践
  • AutoGen Studio驱动的自动化渗透测试工作流重构
  • 3步免费解锁WeMod专业版:终极本地增强工具使用指南
  • 如何从图表图像中提取数据:WebPlotDigitizer完全指南
  • 如何高效使用BilibiliDown:3步轻松下载B站视频的完整指南
  • 【紧急预警】DeepSeek-V2.5已确认存在上下文污染型推理劫持漏洞!48小时内必须完成的3项热补丁操作
  • CTSD算法超参数调优实战:从原理到应用,解决机器翻译重复与幻觉问题
  • Loop窗口管理工具:如何用优雅的径向菜单彻底改变你的Mac工作流
  • 电力负荷预测挑战:Informer2020如何实现长序列时间序列预测的完整解决方案
  • 如何通过SMUDebugTool深度掌控AMD锐龙处理器性能
  • Taotoken官方价折扣与Token Plan套餐的实际节省效果分析
  • 深圳大学“挑战杯“赛事社团协助 工作计划
  • 为Hermes Agent工具配置Taotoken自定义供应商的详细步骤
  • 2026年TK越南站点代运营服务商排名前五专业深度测评 - 羊城派
  • 内蒙古自治区牙克石寄件省钱新思路!全网高性价比寄件渠道汇总,日常发货省心又划算 - 时讯资讯
  • DeepSeek负载均衡失效导致LLM响应延迟飙升300%?紧急回滚+根因分析全流程复盘(含Wireshark抓包关键证据)
  • 为什么92%的DeepSeek部署失败?揭秘量化校准中被忽略的3个KL散度阈值临界点
  • TimesFM终极优化指南:如何将时间序列预测速度提升5倍
  • ChatGPT投资人邮件撰写终极指南:1份可即插即用的合规性Checklist + 3套SEC/VC双审通过话术库
  • 2026年预算2000买白色十字门冰箱,大白405成首选! - 品牌企业推荐师(官方)
  • GIF动画处理工具Gifsicle:如何高效优化与管理动态图像资源
  • 观测对比,接入 Taotoken 前后 API 调用的平均延迟与成功率变化
  • 内蒙古自治区霍林郭勒寄快递省钱指南|多款小众靠谱寄件渠道盘点,全国低价跨省寄送省心又划算 - 时讯资讯
  • BaiduNetdiskPlugin-macOS:突破下载限制的macOS百度网盘优化指南
  • 内蒙古自治区乌兰察布寄快递省钱新思路!4 款小众靠谱寄件渠道,全国发货性价比拉满 - 时讯资讯
  • RAG增强检索在AIGC工作流中的实战:从文档解析到向量召回全流程