微信小程序 res =>的意义及userInfoReadyCallback函数的作用

刚开始接触微信小程序,想写个迷你计算器的小程序,感觉开发挺方便的,当准备使用用户信息时,打算看一下它是怎么获取用户信息的,为之后获取用户openid准备。获取用户信息主要在app.js 和 index.js中

代码中的 res =>可以理解为function(res),其中res即为返回的数据结果对象,不知道又是哪新出的语法规则(后来补充,这个是ES6的箭头函数,https://blog.youkuaiyun.com/zjw_python/article/details/80880208)

app.js

App({
  onLaunch: function () {
    // 展示本地存储能力
    var logs = wx.getStorageSync('logs') || []
    logs.unshift(Date.now())
    wx.setStorageSync('logs', logs)

    // 登录
    wx.login({
      success: res => {
      }
    })
    // 获取用户信息
    wx.getSetting({            
      success: res => {
        if (res.authSetting['scope.userInfo']) {
          // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
          wx.getUserInfo({
            success: res => {
              // 可以将 res 发送给后台解码出 unionId
              this.globalData.userInfo = res.userInfo
              
              // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
              // 所以此处加入 callback 以防止这种情况
              if (this.userInfoReadyCallback) {
                this.userInfoReadyCallback(res)
              }
            }
          })
        }
      }
    })
  },
  globalData: {
    userInfo: null,
  }
})

index.js

/index.js
//获取应用实例
const app = getApp()

Page({
  data: {
    motto: '欢迎使用迷你计算器',
    userInfo: {},
    hasUserInfo: false,
    canIUse: wx.canIUse('button.open-type.getUserInfo')
  },
  //事件处理函数
  bindViewTap: function() {
    wx.navigateTo({
      url: '../logs/logs'
    })
  },
  
  onLoad: function () {
    if (app.globalData.userInfo) {
      this.setData({
        userInfo: app.globalData.userInfo,
        hasUserInfo: true
      })
    } else if (this.data.canIUse){
      // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
      // 所以此处加入 callback 以防止这种情况
      app.userInfoReadyCallback = res => {
        this.setData({
          userInfo: res.userInfo,
          hasUserInfo: true
        })
      }
    } else {
      // 在没有 open-type=getUserInfo 版本的兼容处理
      wx.getUserInfo({
        success: res => {
          app.globalData.userInfo = res.userInfo
          this.setData({
            userInfo: res.userInfo,
            hasUserInfo: true
          })
        }
      })
    }
  },

  getUserInfo: function(e) {
    console.log(e)
    app.globalData.userInfo = e.detail.userInfo
    this.setData({
      userInfo: e.detail.userInfo,
      hasUserInfo: true
    })
  }
})

首先执行的是app.js的wx.getUserInfo,这个是获取用户信息的网络请求,由于其返回结果不知道在index页面加载完成之前还是之后完成,因此分为两种情况:

  • 在index页面加载完成之前返回:此时优先执行app.js中success函数的代码,app.globalData.userInfo就保存了用户信息。运行到这里时,由于userInfoReadyCallback函数是在index.onload中定义的,因此此时该函数并没有被定义,所以不执行该函数。之后执行index.js中onload中的代码,执行第一个if分支,赋值给页面的userInfohasUserInfo
 if (this.userInfoReadyCallback) {
       this.userInfoReadyCallback(res)
    }
  • 在index页面加载完成之后返回:此时优先执行index.js中onload中的代码,由于用户信息还没有返回,所以app.globalData.userInfo为null,执行第二个if分支,定义userInfoReadyCallback函数。随后数据被返回,执行success的代码,app.globalData.userInfo在此时才保存了用户的信息,并执行userInfoReadyCallback函数,赋值给页面的userInfohasUserInfo

因此总体来说userInfoReadyCallback函数的作用,就是保证页面的userInfohasUserInfo被正确赋值,无论用户信息在页面加载完成之前还是之后返回。


如果觉得这篇文章帮助了您,请打赏一个小红包鼓励作者继续创作哦!!!

在这里插入图片描述

// app.js const MiniProgramDataManager = require('./data/MiniProgramDataManager'); App({ onLaunch: function () { // 展示本地存储能力 var logs = wx.getStorageSync('logs') || [] logs.unshift(Date.now()) wx.setStorageSync('logs', logs) // 初始化数据管理器 this.initDataManager(); // 登录 wx.login({ success: res => { // 发送 res.code 到后台换取 openId, sessionKey, unionId } }) // 获取用户信息 wx.getSetting({ success: res => { if (res.authSetting['scope.userInfo']) { // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框 wx.getUserInfo({ success: res => { // 可以将 res 发送给后台解码出 unionId this.globalData.userInfo = res.userInfo // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回 // 所以此处加入 callback 以防止这种情况 if (this.userInfoReadyCallback) { this.userInfoReadyCallback(res) } } }) } } }) }, // 初始化数据管理器 initDataManager: function() { // 创建数据管理器实例 const baseUrl = 'http://192.168.1.11:8080'; // 实际API地址 this.globalData.dataManager = new MiniProgramDataManager(baseUrl); // 初始化数据管理器 this.globalData.dataManager.initialize().catch(error => { console.error('数据管理器初始化失败:', error); wx.showToast({ title: '数据加载失败,请重试', icon: 'none', duration: 2000 }); }); }, // 检查微信登录状态 checkWechatLogin: function() { // 检查本地存储中是否有userId const userId = wx.getStorageSync('userId'); if (userId) { console.log('用户已登录,userId:', userId); this.globalData.needLogin = false; } else { console.log('用户未登录'); this.globalData.needLogin = true; } return !this.globalData.needLogin; }, // 添加登录方法,保存用户信息 login: function(userInfo) { // 保存用户信息到全局数据 this.globalData.userInfo = userInfo; this.globalData.needLogin = false; // 保存用户ID到本地存储 wx.setStorageSync('userId', userInfo.id); wx.setStorageSync('role', userInfo.role); wx.setStorageSync('name', userInfo.name); // 保存登录状态到本地存储 wx.setStorageSync('loginInfo', { isLoggedIn: true, userInfo: userInfo }); }, // 添加微信登录方法 wechatLogin: function() { return new Promise((resolve, reject) => { wx.login({ success: res => { if (res.code) { // 发送code到后台获取用户信息 wx.request({ url: this.globalData.baseUrl + '/users/wechat-login', method: 'POST', data: { code: res.code }, success: loginRes => { if (loginRes.data.status === 200) { // 登录成功 const userInfo = loginRes.data.data; this.login(userInfo); resolve(userInfo); } else if (loginRes.data.status === 404) { // 用户不存在,需要注册 resolve({ openid: loginRes.data.openid, needRegister: true }); } else { // 其他错误 reject(loginRes.data.text || '微信登录失败'); } }, fail: err => { reject('网络错误,请稍后重试'); } }); } else { reject('获取微信授权失败'); } }, fail: err => { reject('微信登录失败'); } }); }); }, // 显示登录选择弹窗 showLoginModal: function(callback) { wx.showModal({ title: '应用名称', content: '请选择登录方式', confirmText: '微信登录', cancelText: '账号登录', success: res => { if (res.confirm) { // 用户点击了微信登录 wx.showLoading({ title: '登录中...', }); this.wechatLogin() .then(result => { wx.hideLoading(); if (result.needRegister) { // 需要注册,跳转到注册页面并传递openid wx.navigateTo({ url: `/pages/register/register?openid=${result.openid}` }); } else { // 登录成功,执行回调 if (typeof callback === 'function') { callback(true); } } }) .catch(error => { wx.hideLoading(); wx.showToast({ title: error, icon: 'none', duration: 2000 }); }); } else { // 用户点击了账号登录 wx.navigateTo({ url: '/pages/login/login' }); } } }); }, // 检查登录状态,如果未登录则显示登录选择弹窗 checkLoginStatus: function(pageObj) { if (!this.checkWechatLogin()) { console.log('未登录,显示登录选择弹窗'); this.showLoginModal(() => { // 登录成功后的回调,可以刷新页面或执行其他操作 if (pageObj && typeof pageObj.onLoad === 'function') { pageObj.onLoad(pageObj.options); } }); return false; } return true; }, globalData: { userInfo: null, dataManager: null, needLogin: true, baseUrl: 'http://192.168.1.11:8080' } })在全局app中注册
最新发布
07-31
<think>我们已经在App的onLaunch中初始化了dataManager,并将其挂载到globalData上。现在,我们需要在需要使用dataManager的页面中获取它。 在页面中,我们可以通过getApp()来获取全局的App实例,进而访问globalData.dataManager。 但是,为了确保在页面加载时dataManager已经初始化,我们可以在页面的onLoad生命周期中检查dataManager是否存在,如果不存在则等待其初始化。 然而,由于dataManager的初始化是异步的(在App的onLaunch中调用initialize()),因此我们需要一种机制来确保在页面中使用dataManager时它已经准备好了。 我们可以通过以下方式解决: 1. 在App中维护一个dataManagerReady的Promise,在初始化完成后resolve。 2. 在页面中,我们可以在onLoad中等待这个Promise。 让我们修改app.js,添加一个dataManagerReady的Promise: 在app.js中: ```javascript App({ onLaunch: function () { // ... 其他代码 ... // 初始化数据管理器 this.initDataManager(); // ... 其他代码 ... }, initDataManager: function() { const baseUrl = 'http://192.168.1.11:8080'; // 实际API地址 this.globalData.dataManager = new MiniProgramDataManager(baseUrl); // 创建一个Promise,用于表示dataManager是否初始化完成 this.globalData.dataManagerReady = new Promise((resolve, reject) => { this.globalData.dataManager.initialize() .then(() => { resolve(); }) .catch(error => { console.error('数据管理器初始化失败:', error); reject(error); }); }); }, // ... 其他方法 ... }); ``` 然后,在页面中,我们可以在onLoad中使用这个Promise: ```javascript Page({ onLoad: function(options) { const app = getApp(); app.globalData.dataManagerReady.then(() => { // 在这里可以安全地使用app.globalData.dataManager this.dataManager = app.globalData.dataManager; // 然后执行页面的初始化逻辑 this.initPage(); }).catch(error => { // 处理初始化失败的情况 wx.showToast({ title: '数据加载失败', icon: 'none' }); }); }, // ... 其他方法 ... }); ``` 但是,请注意,如果dataManager初始化失败,则每次进入页面都会显示错误。我们可能希望只在第一次初始化失败时显示错误,然后允许用户重试。 另外,我们也可以考虑在页面中直接调用dataManager的initialize方法(如果它还没有初始化的话),但这样会导致多次初始化。所以最好在App中只初始化一次,然后页面等待这个初始化完成。 现在,我们修改app.js,添加dataManagerReady的Promise。 由于我们已经在app.js中调用了initDataManager,并且initDataManager中已经创建了dataManagerReady,所以我们在页面中就可以等待这个Promise。 下面我们修改app.js,按照上面的方式添加dataManagerReady。 同时,我们还需要在页面中(比如tianjia页面)修改onLoad方法,等待dataManagerReady。 但是,请注意,如果用户第一次打开小程序,在app.js的onLaunch中初始化dataManager,然后进入tianjia页面,在tianjia页面的onLoad中等待dataManagerReady,这是可行的。 然而,如果用户已经打开过小程序,然后再次进入tianjia页面,此时dataManager已经初始化完成,那么dataManagerReady这个Promise已经是resolved状态,所以then回调会立即执行。 因此,这种方式是可行的。 现在,我们修改app.js,添加dataManagerReady。同时,修改tianjia页面的onLoad,等待dataManagerReady。 由于代码较长,我只给出修改的部分: 修改app.js: ```javascript // app.js const MiniProgramDataManager = require('./data/MiniProgramDataManager'); App({ onLaunch: function () { // ... 其他代码 ... // 初始化数据管理器 this.initDataManager(); // ... 其他代码 ... }, initDataManager: function() { const baseUrl = 'http://192.168.1.11:8080'; // 实际API地址 this.globalData.dataManager = new MiniProgramDataManager(baseUrl); // 将dataManagerReady作为全局数据的一个属性,是一个Promise this.globalData.dataManagerReady = this.globalData.dataManager.initialize().catch(error => { console.error('数据管理器初始化失败:', error); wx.showToast({ title: '数据加载失败,请重试', icon: 'none', duration: 2000 }); // 这里我们仍然将错误抛出,以便页面可以捕获 throw error; }); }, // ... 其他方法 ... globalData: { userInfo: null, dataManager: null, dataManagerReady: null, // 初始为null,在initDataManager中赋值 needLogin: true, baseUrl: 'http://192.168.1.11:8080' } }); ``` 然后,在tianjia页面的onLoad中: ```javascript // pages/tianjia/tianjia.js Page({ onLoad: function(options) { const app = getApp(); app.globalData.dataManagerReady.then(() => { this.dataManager = app.globalData.dataManager; this.initPage(); // 调用页面初始化方法 }).catch(error => { // 可以在这里显示错误信息,并提供重试按钮 wx.showToast({ title: '数据加载失败,请退出重试', icon: 'none' }); }); }, // ... 其他方法 ... }); ``` 这样,我们就确保了在页面中使用dataManager时,它已经初始化完成。 另外,我们还需要在tianjia页面中,将原来在onLoad中调用的initPage移到then回调中。 注意:在tianjia页面的onLoad中,我们原来有权限检查,这个检查不应该等待dataManagerReady,因为权限检查不依赖于数据。所以我们可以先做权限检查,然后再等待dataManager初始化。 ```javascript onLoad: function(options) { if (!this.checkPermission()) return; const app = getApp(); app.globalData.dataManagerReady.then(() => { this.dataManager = app.globalData.dataManager; this.initPage(); // 调用页面初始化方法 }).catch(error => { // 错误处理 }); }, ``` 这样修改后,权限检查通过后,才会等待dataManager初始化。 最后,由于我们已经在app.js中初始化了dataManager,所以不需要在页面中再调用initialize,但是我们需要在页面中设置dataManager的callback,以便在数据更新时刷新页面。 在tianjia页面的initPage方法中,我们注册了回调: ```javascript initPage: function() { this.dataManager.registerCallback('all', this.handleDataUpdate.bind(this)); this.refreshDingdanList(); this.refreshBancaiList(); }, ``` 这样,当任何数据更新时,都会触发handleDataUpdate。 综上所述,我们通过一个全局的Promise来确保dataManager初始化完成后再进行页面数据操作,避免了数据未初始化导致的错误。 现在,请根据上述思路修改代码。</think>在全局App中注册数据管理器和登录功能是微信小程序开发中的常见模式。下面我将提供一个完整的解决方案,包括在App.js中注册全局数据管理器、登录状态管理和弹窗系统。 ### 完整解决方案 ```javascript:app.js const MiniProgramDataManager = require('./data/MiniProgramDataManager'); const modalManager = require('./components/modal-registry/modal-manager'); // 引入弹窗管理器 const { registerModal } = require('./components/modal-registry/modal-registry'); // 引入弹窗注册函数 App({ onLaunch: function () { // 初始化数据管理器 this.initDataManager(); // 初始化弹窗系统 this.initModalSystem(); // 检查登录状态 this.checkWechatLogin(); }, // 初始化数据管理器 initDataManager: function() { const baseUrl = 'http://192.168.1.11:8080'; this.globalData.dataManager = new MiniProgramDataManager(baseUrl); // 初始化数据管理器 this.globalData.dataManager.initialize().catch(error => { console.error('数据管理器初始化失败:', error); wx.showToast({ title: '数据加载失败,请重试', icon: 'none', duration: 2000 }); }); }, // 初始化弹窗系统 initModalSystem: function() { // 注册弹窗 registerModal('dingdan', { /* 订单弹窗配置 */ }); registerModal('dingdan_chanpin', { /* 订单-产品弹窗配置 */ }); registerModal('chanpin', { /* 产品弹窗配置 */ }); // ...注册其他弹窗 // 将弹窗管理器挂载到全局 this.globalData.modalManager = modalManager; }, // 检查微信登录状态 checkWechatLogin: function() { const userId = wx.getStorageSync('userId'); this.globalData.needLogin = !userId; }, // 登录方法 login: function(userInfo) { // 保存用户信息到全局数据 this.globalData.userInfo = userInfo; this.globalData.needLogin = false; // 保存用户ID到本地存储 wx.setStorageSync('userId', userInfo.id); wx.setStorageSync('role', userInfo.role); wx.setStorageSync('name', userInfo.name); // 保存登录状态到本地存储 wx.setStorageSync('loginInfo', { isLoggedIn: true, userInfo: userInfo }); }, // 微信登录方法 wechatLogin: function() { return new Promise((resolve, reject) => { wx.login({ success: res => { if (res.code) { wx.request({ url: this.globalData.baseUrl + '/users/wechat-login', method: 'POST', data: { code: res.code }, success: loginRes => { if (loginRes.data.status === 200) { const userInfo = loginRes.data.data; this.login(userInfo); resolve(userInfo); } else if (loginRes.data.status === 404) { resolve({ openid: loginRes.data.openid, needRegister: true }); } else { reject(loginRes.data.text || '微信登录失败'); } }, fail: err => { reject('网络错误,请稍后重试'); } }); } else { reject('获取微信授权失败'); } }, fail: err => { reject('微信登录失败'); } }); }); }, // 显示登录选择弹窗 showLoginModal: function(callback) { wx.showModal({ title: '应用名称', content: '请选择登录方式', confirmText: '微信登录', cancelText: '账号登录', success: res => { if (res.confirm) { wx.showLoading({ title: '登录中...' }); this.wechatLogin() .then(result => { wx.hideLoading(); if (result.needRegister) { wx.navigateTo({ url: `/pages/register/register?openid=${result.openid}` }); } else if (typeof callback === 'function') { callback(true); } }) .catch(error => { wx.hideLoading(); wx.showToast({ title: error, icon: 'none', duration: 2000 }); }); } else { wx.navigateTo({ url: '/pages/login/login' }); } } }); }, // 检查登录状态 checkLoginStatus: function(pageObj) { if (!this.checkWechatLogin()) { this.showLoginModal(() => { if (pageObj && typeof pageObj.onLoad === 'function') { pageObj.onLoad(pageObj.options); } }); return false; } return true; }, globalData: { userInfo: null, dataManager: null, // 全局数据管理器 modalManager: null, // 全局弹窗管理器 needLogin: true, baseUrl: 'http://192.168.1.11:8080' } }); ``` ### 代码解释 #### 1. 初始化数据管理器 ```javascript initDataManager: function() { const baseUrl = 'http://192.168.1.11:8080'; this.globalData.dataManager = new MiniProgramDataManager(baseUrl); this.globalData.dataManager.initialize().catch(error => { // 错误处理 }); }, ``` - 创建并初始化全局数据管理器 - 使用Promise处理异步初始化过程 - 提供错误处理机制 #### 2. 初始化弹窗系统 ```javascript initModalSystem: function() { // 注册弹窗 registerModal('dingdan', { /* 配置 */ }); registerModal('dingdan_chanpin', { /* 配置 */ }); // 将弹窗管理器挂载到全局 this.globalData.modalManager = modalManager; }, ``` - 注册应用所需的所有弹窗类型 - 将弹窗管理器暴露到全局,方便页面使用 #### 3. 登录管理 ```javascript // 登录方法 login: function(userInfo) { // 保存用户信息 this.globalData.userInfo = userInfo; this.globalData.needLogin = false; // 保存到本地存储 wx.setStorageSync('userId', userInfo.id); // ...其他信息 }, // 微信登录 wechatLogin: function() { return new Promise((resolve, reject) => { wx.login({ success: res => { // 处理登录逻辑 } }); }); }, ``` - 封装登录逻辑,支持普通登录和微信登录 - 使用Promise管理异步登录过程 - 保存登录状态到全局和本地存储 #### 4. 登录状态检查 ```javascript checkLoginStatus: function(pageObj) { if (!this.checkWechatLogin()) { this.showLoginModal(() => { // 登录成功后的回调 }); return false; } return true; }, ``` - 检查用户是否已登录 - 未登录时显示登录弹窗 - 提供登录成功后的回调机制 #### 5. 全局数据 ```javascript globalData: { userInfo: null, dataManager: null, // 全局数据管理器 modalManager: null, // 全局弹窗管理器 needLogin: true, baseUrl: 'http://192.168.1.11:8080' } ``` - `dataManager`: 全局数据管理实例 - `modalManager`: 全局弹窗管理实例 - `userInfo`: 当前用户信息 - `needLogin`: 是否需要登录标志 - `baseUrl`: API基础地址 ### 在页面中使用全局功能 ```javascript:pages/index/index.js Page({ onLoad: function() { const app = getApp(); // 检查登录状态 if (!app.checkLoginStatus(this)) return; // 使用全局数据管理器 app.globalData.dataManager.getData('dingdans').then(data => { this.setData({ dingdans: data }); }); // 使用全局弹窗管理器 this.showAddModal = function() { app.globalData.modalManager.show('dingdan').then(result => { // 处理弹窗返回结果 }); }; } }); ``` ### 最佳实践建议 1. **错误处理增强**: ```javascript initDataManager: function() { // ...初始化代码... .catch(error => { console.error('数据初始化失败:', error); // 提供重试机制 wx.showModal({ title: '初始化失败', content: '数据加载失败,是否重试?', success: (res) => { if (res.confirm) this.initDataManager(); } }); }); }, ``` 2. **弹窗配置集中管理**: ```javascript // 创建 modal-configs.js 文件 module.exports = { dingdan: { /* 订单弹窗配置 */ }, dingdan_chanpin: { /* 订单-产品弹窗配置 */ }, // ...其他弹窗配置 }; // 在app.js中使用 const modalConfigs = require('./modal-configs'); initModalSystem: function() { Object.keys(modalConfigs).forEach(modalName => { registerModal(modalName, modalConfigs[modalName]); }); // ...其他代码... }, ``` 3. **登录状态监听**: ```javascript // 添加全局登录状态监听器 this.globalData.loginListeners = []; // 添加监听方法 addLoginListener: function(callback) { this.globalData.loginListeners.push(callback); }, // 登录成功后触发监听 login: function(userInfo) { // ...现有代码... // 触发所有监听器 this.globalData.loginListeners.forEach(callback => { if (typeof callback === 'function') callback(userInfo); }); }, ``` 4. **权限管理增强**: ```javascript // 检查用户角色 checkRole: function(requiredRole) { const role = parseInt(wx.getStorageSync('role') || '0'); return role >= requiredRole; }, // 在页面中使用 onLoad: function() { if (!app.checkRole(2)) { // 需要2级以上权限 wx.showToast({ title: '权限不足', icon: 'none' }); return; } // ...其他代码... } ``` ### 相关问题
评论 5
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值