分类: 微信小程序

  • 微信小程序页面启动,如何获取全局的用户信息

    在微信小程序中,App.onLaunchPage.onLoad 是两个生命周期方法。App.onLaunch 是当小程序启动时触发的方法,而 Page.onLoad 是当页面加载时触发的方法。因为 App.onLaunchPage.onLoad 是异步执行的,有时 Page.onLoad 会在 App.onLaunch 完成之前执行。这可能导致页面在获取用户信息之前就已经加载完成。

    this.userInfoReadyCallback 是一种解决方案,用于处理这种情况。它是一个回调函数,用于在全局数据(例如用户信息)准备就绪时通知页面。

    原理如下:

    1. App.onLaunch 方法中,你会尝试获取用户信息并将其存储到全局变量 globalData.userInfo 中。
    2. 由于 wx.getUserInfo 是一个网络请求,它可能在 Page.onLoad 之后才返回。因此,在 App.onLaunch 中,你可以检查是否已经定义了 this.userInfoReadyCallback。如果已经定义了,说明页面已经加载完毕,并且需要在用户信息准备就绪时接收通知。
    3. 在需要用户信息的页面的 Page.onLoad 方法中,你可以检查全局变量 globalData.userInfo 是否已经存在。如果不存在,说明用户信息还未准备就绪,此时你可以定义 this.userInfoReadyCallback 并将其设置为一个处理用户信息的函数。

    这样,当用户信息准备就绪时,App.onLaunch 方法会调用 this.userInfoReadyCallback,从而通知页面并处理用户信息。这种方法确保了页面在处理用户信息时,不会因为数据未准备就绪而出现错误。

    小程序默认的app.js

    // app.js
    App({
      onLaunch: function () {
        // 展示本地存储能力
        var logs = wx.getStorageSync('logs') || []
        logs.unshift(Date.now())
        wx.setStorageSync('logs', logs)
    
        // 登录
        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)
                  }
                }
              })
            }
          }
        })
      },
      globalData: {
        userInfo: null
      }
    })
    

    如果想确保在page页面能获取用户信息,需要判断`app.globalData.userInfo`是否存在,不存在的话调用app.userInfoReadyCallback

    // pages/somePage/somePage.js
    Page({
      data: {
        userInfo: null
      },
    
      onLoad: function () {
        // 获取全局 App 实例
        const app = getApp();
    
        // 检查全局数据 globalData.userInfo 是否已经存在
        if (app.globalData.userInfo) {
          // 用户信息已经存在,可以直接在页面中使用
          this.setData({
            userInfo: app.globalData.userInfo
          });
        } else {
          // 用户信息不存在,定义一个处理用户信息的函数
          app.userInfoReadyCallback = (res) => {
            this.setData({
              userInfo: res.data.data
            });
          };
        }
      },
    
      // 其他页面方法...
    });
    
  • 微信小程序用户修改昵称不生效

    一 问题重现

    在开发微信小程序时,我遇到了一个问题。当用户手动输入昵称并提交时,一切正常。

    然而,在这情况下会出现问题。当用户点击输入框时,微信系统会提示“用微信昵称”。若用户选择此提示,昵称输入框会自动填充当前微信昵称,然后点击保存按钮,实际上nickname输入框是没有数据的。

    问题重现一下,在input输入框上我添加了监听事bindinput=”onChangeNickname”.

    如下:

    <form bindsubmit="save">
    ...
    <input type="nickname" class="weui-input" name="nickname" placeholder="请输入昵称"  bindinput="onChangeNickname" value="{{nickname}}"/>
    ...
    <button form-type="submit" type="primary" style="margin-top: 100rpx;">保存</button>
    </form>

    js逻辑如下:

    Page({
      data: {
        nickname:'',
      },
      onChangeNickname(e:any){
       const nickname = e.detail.value;
       this.setData({nickname})
      },
      save() {
        const nickname = this.data.nickname;
        wx.request({
          ...
          //api逻辑
        })
      }
    })

    这个监听事件只在用户正常输入时起作用,如果想上面说的那样选择系统的“用微信昵称”,其实这个nickname是没变化的。

    二 操作如虎

    (这段也没解决问题,如果想解决问题直接第三段)

    1,审核完后出发函数

    从小程序的文档获得input的一个属性bindnicknamereview,不管是输入还是自动填充,在审核完昵称后触发bindnicknamereview

    bindnicknamereview用户昵称审核完毕后触发,仅在 type 为 “nickname” 时有效,event.detail = { pass, timeout }

    天真的我以为昵称审核完后,触发事件bindnicknamereview,会返回昵称,然而并没有,所以这个方式也不行。

    2,提交

    然后我一通搜索,在”表单组件form“文档发现这么一句话

    当点击 form 表单中 form-type 为 submit 的 button 组件时,会将表单组件中的 value 值进行提交,需要在表单组件中加上 name 来作为 key。

    这样的话获取输入框的内容,就不需要从data获取了,直接我们从bindsubmit这个监听来获取nickname以及其他的参数。

    save(e) {
        const { nickname, avatar } = e.detail.value;
        wx.request({
          ...
          //api逻辑
        })
    }

    三,解决问题

    //wxml
    <!--pages/set-profile/index.wxml 设置用户的头像和昵称-->
    <view>
      <form bindsubmit="save">
        <!-- 头像 -->
        <button class="avatar-wrapper" open-type="chooseAvatar" bind:chooseavatar="onChooseAvatar">
          <image class="avatar" src="{{avatar}}"></image>
        </button> 
        <!-- 头像隐藏域 -->
        <input type="text" name="avatar" value="{{avatar}}" type="text" hidden="true" />
        <mp-form>
          <mp-cells>
            <mp-cell title="昵称">
              <input type="nickname" class="weui-input" name="nickname" placeholder="请输入昵称" value="{{nickname}}"/>
            </mp-cell>
          </mp-cells>
        </mp-form>
        <button form-type="submit" type="primary" style="margin-top: 100rpx;">保存</button>
      </form>
    </view>
    
    import config from "../../wxconfig";
    
    const defaultAvatarUrl = "https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0";
    
    Page({
      data: {
        token: "",
        nickname: "",
        avatar: "",
        userProfile: {},
      },
      
      onLoad() {
        const token = wx.getStorageSync("token");
        const userProfile = wx.getStorageSync("userProfile");
        let avatar, nickname;
        if (userProfile) {
          avatar = userProfile.avatar;
          nickname = userProfile.nickname;
        } else {
          avatar = defaultAvatarUrl;
          nickname = "";
        }
        this.setData({
          token,
          nickname,
          avatar,
          userProfile,
        });
      },
    
      onChooseAvatar(e) {
        const { avatarUrl } = e.detail;
        wx.uploadFile({
          url: config.baseUrl + "/api/v1/upload",
          filePath: avatarUrl,
          name: "file",
          header: {
            Authorization: "Bearer " + this.data.token,
          },
          formData: {
            mediaType: "avatar",
          },
          success: (res) => {
            const mydata = JSON.parse(res.data);
            if (mydata.status == 1) {
              const newAvatar = mydata.data.url;
              this.setData({
                avatar: newAvatar,
              });
            } else {
              wx.showToast({
                title: "上传失败",
                icon: "error",
              });
            }
          },
          fail: (err) => {
            wx.showToast({
              title: err.message,
              icon: "error",
            });
          },
        });
      },
    
      save(e) {
        const { nickname, avatar } = e.detail.value;
        wx.request({
          method: "POST",
          url: config.baseUrl + "/api/v1/setprofile",
          header: {
            Authorization: "Bearer " + this.data.token,
          },
          data: {
            nickname,
            avatar,
          },
          success: (res) => {
            if (res.data.status == 1) {
              wx.showToast({
                title: "保存成功",
                icon: "success",
                duration: 2000,
              });
              const userProfile = { ...this.data.userProfile, avatar, nickname };
              this.setData({
                userProfile,
              });
              wx.setStorageSync("userProfile", userProfile);
            }
          },
        });
      },
    });