微信小程序开发采坑记录1

问题1:

在微信小程序开发过程中,在获取userinfo或其他异步处理函数的过程中采用this碰到一些问题,问题如下:

我的login的success回调函数如下:

success: function(res) {
              if(res.statusCode == 200) {
                console.log(res.data)
                this.globalData.sessionId = res.data.sessionId
              }

运行时,此处的this会报错如下:

Cannot read property 'globalData' of undefined;at App getData function;at api operateWXData success callback function

具体截图如下图所示:

然后我查看了app.js另外一处的success回调函数

success: res => {
              // 可以将 res 发送给后台解码出 unionId
              this.globalData.userInfo = res.userInfo

              // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
              // 所以此处加入 callback 以防止这种情况
              if (this.userInfoReadyCallback) {
                this.userInfoReadyCallback(res)
              }

而此处的this有效,我怀疑是success: function(res) {} 和 success: res => {}的区别,于是我修改了第一处的代码为:

success: res => {
              if(res.statusCode == 200) {
                console.log(res.data)
                this.globalData.sessionId = res.data.sessionId
              }

执行成功!如果不想修改回调函数,可以在函数定义之前增加var that = this,然后此处修改this为that即可。

分析原因:

此处代码中的res =>{ }与普通的function(res){}函数定义功能相同,其中res即为形参对象,但前者不同的是针对this的处理上。前者是采用的是ECMAScript 6.0(关于ES 6.0与Javascript之间的关系详见:https://blog.csdn.net/u012331525/article/details/80517488)的新特性:箭头函数(关于箭头函数的详细介绍详见:https://blog.csdn.net/weixin_41593408/article/details/86771813)。

箭头函数有以下几点注意事项:

  • 函数体内的this对象就是定义时所在的对象,而不是调用时所在的对象
  • 不可以作为构造函数
  • 不可以使用arguments对象
  • 不可以使用yield命令,因此箭头函数不能用作 Generator 函数

普通函数如下代码:

var obj = {
  x:1,
  func: function(){ console.log(this.x) },  // 此处的 this 代表 obj
  test: function(){
    setTimeout(function(){
      alert(this);  // 因为使用了异步,在运行过程中,this发生了指针转移,不再指向obj,而是指向全局 Window对象
      this.func();
    },1000);
  }
};
obj.test(); 

//报错
 // TypeError: this.func is not a function

上述代码中,在test函数中定义了一个延时调用函数,其实际执行时间为1000ms之后,此时test已经执行完毕,当延时函数执行时,this值为全局window对象,而全局的window对象不存在func函数,所以报错。

箭头函数代码如下:

var obj = {
  x: 1,
  func: function(){ console.log(this.x) },
  test: function(){
    setTimeout(() => {
      alert(this); // [object Object]  此处的this指向obj
      this.func();
    },1000);
  }
};
 
obj.test();  // 这回this就指向obj了

同样在test中定义延时调用,但由于箭头函数特性,使其this值总是指向函数定义生效时所在对象,此处即为obj,所以不会出现上述错误。其本质要点在与实际上,箭头函数自身没有自己的this,导致其内部的this就是外层代码块的this

在微信小程序的开发工具中,已经有ES6语法转换ES5的设置,如下:

问题2:关于异步加载数据及回调函数(Callback)理解

小程序中大部分网络交互的接口实现都是异步的,因此在写小程序时,不可避免的就必须和异步进行打交道。微信小程序基本是通过Javascript+wxss+wxml(类html)组合而成,小程序中的主体逻辑是用js实现的。对于精通前端技术的人来说,javascript中的promise、callback应该是非常熟悉了,这部分我来说说自己对小程序中如何使用callback及promise实现异步处理和回调的理解及总结。我们以微信小程序中获取用户信息userInfoReadyCallback为例来说明:

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分支,赋值给页面的userInfo和hasUserInfo
  • 在index页面加载完成之后返回:此时优先执行index.js中onload中的代码,由于用户信息还没有返回,所以app.globalData.userInfo为null,执行第二个if分支,定义userInfoReadyCallback函数。随后数据被返回,执行success的代码,app.globalData.userInfo在此时才保存了用户的信息,并执行userInfoReadyCallback函数,赋值给页面的userInfo和hasUserInfo

注:因为wx.getUserInfo是异步网络请求,不知道异步请求先执行完毕还是page.onload先执行完毕,判断的关键在于userInfoReadyCallback方法是否定义。

1. 方法(userInfoReadyCallback)如果定义了,则说明page.onload比当前方法运行的早(page已经完成初始化),app的globalData还没有数据,通过此回调可以及时的刷新数据

2. 方法如果没有定义,则说明page.onload比当前方法运行的晚(page还没有初始化),app的globalData是有值的,可以在page.onload中取globalData里面的值

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

所谓回调(callback)就是在异步调用时,当异步处理完成后要执行的代码块。

 

假设需求:

  1. 首先在数据库的medicine表中有3条药品信息记录,每条记录简单的包含药品的一些基础信息。
  2. 在小程序中,我们需要从medicine表中查出所有药品记录,并在页面中展示

在小程序中,微信提供的数据库访问接口都是异步的,因此不能用简单的同步模式写代码,这时就需要使用callback或者promise

我们先来看看以传统的同步模式来实现的话代码会写成如下形式:

分析如下:

按照同步模式的理解,以上代码第8行,调用this.getDbData函数查询数据库,在函数内将查询结果赋值给全局变量data中的medicine中,并输出查询结果。

第9行,在调用了getDbData函数之后,再输出变量data的内容,发现medicine变量并没有被赋值,如下图

从上边的执行结果可以看到,第9行先执行了,而getDbData中的输出后执行了,这就是因为getDbData中调用数据库的接口,在微信API中是异步实现的,因此要想实现查询获取了相关数据之后,再对数据做进一步处理就需要使用callback机制或者promise

首先,我们通过callback机制来改造如上同步调用模式为异步调用模式:

如上,在第8行增加了this.process_data作为callback传入getDbData函数中第12~14行,是callback函数的定义,并在该函数中输出相关全局变量的内容,第25行,是执行完数据库查询并且完成全局变量赋值之后,将全局变量传给callback函数并调用,整体执行结果如下:

我们可以如上文userInfoReadyCallback一样,通过判断所需数据是否为空,在回调函数中实现数据在页面中加载。

接下来,我们通过promise模式改造上述需求,代码如下:

  1. 如上,第15行重新定义了promise模式的函数,getDataByPromise返回的是一个promise对象
  2. 在第7行调用函数之后,在then中执行相关后置动作,具体执行效果如下:

完整代码如下:

Page({
  data: {
    "medicine": "N/A",
  },
  onLoad: function (options) {
    // callback模式处理异步调用
    // var that = this;
    // this.getDbData("medicine", {}, "medicine", this.process_data)
    // console.log("in onLoad:", this.data)

    // promise模式处理异步调用
    this.getDataByPromise("medicine", {}).then((data) => { 
      this.setData({
        "medicine": data
      })
      console.log("in onLoad promise:", this.data)
    })
  },

  getDataByPromise: function (coll_name, search_cond) {
    var promise = new Promise((resolve, reject) => {
      var that = this;
      const db = wx.cloud.database();
      db.collection(coll_name).where(search_cond).get({
        success: function (res) {
          console.log("in promise info:", res.data)
          resolve(res.data)
        },
        error: function (e) {
          console.log(e)
          reject("查询数据库失败")
        }
      });
    });
    return promise;
  },

  process_data: function(data){
    console.log("in onLoad callback:", this.data)
  },
  getDbData: function (coll_name, search_cond, data_key, cb) {
    const db = wx.cloud.database()
    var that = this;
    var ready_data = {};
    db.collection(coll_name).where(search_cond).get({
      success: function (res) {
        ready_data[data_key] = res.data;
        that.setData(ready_data);
        console.log("查询数据库完成:", that.data, res.data);
        cb(that.data)
      }
    })
  }
})

参考文档

https://blog.csdn.net/iot_player/article/details/87031698

https://blog.csdn.net/zjw_python/article/details/80641963

https://www.cnblogs.com/NKnife/p/6283605.html

https://blog.csdn.net/zjw_python/article/details/80880208

https://blog.csdn.net/mengalong/article/details/83541710

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章