客户反映小程序存在选择门店没有反应的情况,一开始怀疑是前一天修改了 js 代码有问题。用开发工具打开看了一下,点击选择门店的按钮时 console 栏确实有输出错误信息:Cannot read property 'scope.userLocation' of undefined;

选择门店涉及到用户位置信息授权 scope.userLocation,以下是一般情况下用户授权的示例的代码:

// 可以通过 wx.getSetting 先查询一下用户是否授权了 "scope.userLocation" 这个 scope
wx.getSetting({
  success(res) {
    if (!res.authSetting['scope.userLocation']) {
      wx.authorize({
        scope: 'scope.userLocation',
        success (res) {
          // 用户已经同意小程序使用当前的地理位置功能,后续调用 wx.getLocation 接口不会弹窗询问
          res && (res.authSetting["scope.userLocation"] ? 
          wx.getLoaction({
            success: function (res) {
              longitude = res.longitude, latitude = res.latitude;
            }
          }) : getApp().core.showToast({
            title: "您取消了授权",
            image: "/images/icon-warning.png"
          }));
        }
      })
    }
  }
})

以上代码为还原代码,原始代码对获取授权的 getSetting()authorize() 做了封装,但结构上面是一致的。

外层依次调用了 getSetting()authorize() 方法,参数都是 res ,最后到获取位置信息时,又做了一次权限判断。这时候取到的参数 res 是 authorize() 返回的,我在外面两层的 success 回调里依次输出参数 res 的内容,getSetting() 获取到的是(未授权过位置权限):

{
authSetting: {}
errMsg: "getSetting:ok"
__proto__: Object
}

authorize() 获取到的是:

{
errMsg: "authorize:ok"
__proto__: Object
}

显然,authorize() 方法返回值是不包含 authSetting 对象的,代码原作者目的应该是获取到 getSetting 的返回值,但在第一次获取授权设置时,authSetting 是空的,也就是说,以这样的逻辑注定是获取不到授权后的授权设置的(因为这不单单是参数名重叠被覆盖的问题)。

原作者做二次判断 authSetting 的目的其实更多是完善逻辑,在用户取消时,弹出自定义模态框,提示用户取消了授权。这样的逻辑注定落空了,因为 authorize() 的 success 回调代表了用户已经同意了授权,判断逻辑分支无效。

既然判断逻辑没有意义,干脆去掉即可:

// 可以通过 wx.getSetting 先查询一下用户是否授权了 "scope.userLocation" 这个 scope
wx.getSetting({
  success(res) {
    if (!res.authSetting['scope.userLocation']) {
      wx.authorize({
        scope: 'scope.userLocation',
        success (res) {
          // 用户已经同意小程序使用当前的地理位置功能,后续调用 wx.getLocation 接口不会弹窗询问
          wx.getLoaction({
            success: function (res) {
              longitude = res.longitude, latitude = res.latitude;
            }
          });
        }
      })
    }
  }
})

[notice]微信小程序 API 调用存在先后顺序,一般会嵌套调用。这时候需要注意返回值参数命名问题,如果变量名相同,上一级的返回值会被覆盖。[/notice]

但这个报错并不会导致选择门店功能无法使用,因为位置授权毕竟是成功了的。再次点击时,authSetting 中已经包含了 userlocation 这一项:

{
authSetting:{
scope.userLocation: true
__proto__: Object
}
errMsg: "getSetting:ok"
__proto__: Object
}

因为 res.authSetting['scope.userLocation'] 已经存在,不需要再次授权,此时返回的 res 就是包含了 scope.userLocation 项的 getSetting() 返回值(源码中做了封装,在 getSetting 回调中判断是否已存在授权设置,存在则直接传值给调用封装的对象的回调 t.success && t.success(e);)。

所以第二次点击选择门店是可以成功的,调试没有发现任何问题。那为什么存在这样的用户一直无法使用位置呢?

猜想是不是手机本身或者微信这个 app 的位置服务被关闭了。尝试关闭位置服务,果然调用失败,授权失败。

总结:调用位置服务时,要确保微信位置服务已启用。