Vue 爬坑之旅 -- history 路由模式下微信分享爬坑总结

现在做 H5 开发,微信平台基本是一个绕不过去的坑,这里面又分为微信授权和微信分享。说它坑,主要提现在以下几个方面:

  1. 文档不够清晰详细。开发文档这块是国内所有互联网公司的通病,文档写的不清不楚,长期不更新,示例代码老旧等等。
  2. 调试困难,微信相关功能调试起来是真的麻烦,所有做过这方面开发调试的朋友应该都深有体会。

这篇文章我们来说下微信分享这块功能的实现以及其中需要注意的几个比较坑的地方在哪里。
在开始之前,有一些准备工作需要做,就是申请公众号,绑定域名,申请 AppId,引入 js-sdk 等,这些具体的请参照微信文档,这些并不是这篇文章要讲的内容。微信JS-SDK说明文档

微信分享流程

微信分享的流程为:

  1. 进入某个页面后将该页面的 url 通过 sha1 算法加密后传给微信,微信会返回一个验证签名,这个签名是能否成功分享的关键。这里需要后端配合,前端无法独自完成。
  2. 在从后端拿到返回的签名配置后,需要调用 js-sdk 的 wx.config 将拿到的配置进行 js-sdk 的初始化。
  3. 在 wx.config 初始化完成后需要调用 js-sdk 的 wx.ready 方法配置需要自定义的分享内容。

上面的过程全部顺利执行完成的话,当用户在微信内使用分享到朋友或朋友圈时就可以正确的分享出想要分享的内容了。

容易出问题的地方

大部分分享功能失败可能都是因为签名的原因,即提示 config:invalid signature ,这个错误一般都是由于 url 传递不正确导致的,当然也可能有其他的原因。请参照下面的这两篇文章。
微信分享JSSDK-invalid signature签名错误的解决方案
微信分享链接出现config:invalid signature错误的解决方法

我在做这功能的时候也碰到了这个问题,首先我去验证了下签名,使用 微信 JS 接口签名校验工具 来进行签名验证,验证后发现签名没有问题。那么大概率就是传给后台签名用的 url 出了问题(最后发现确实也是 url 出了问题)

vue-router 最好使用 history 模式

vue-router 默认采用的是 hash 模式,这种模式下路由后面会加上一个 # 号,在传给微信请求签名的时候需要把这个 # 去掉,比如:

location.href.split('#')[0]

所以最好把 vue-router 设为 history 模式,以下的内容都是基于 history 模式来说的。

排查 url 错误

因为微信要求签名使用的 url 要是动态的,也就是说每次路由切换,url 改变的时候都需要重新签名,针对这个要求很容易就想到在全局的路由钩子里面来做处理。
一开始我是在 router.afterEach 钩子函数里面通过 location.href 来获取 url,我天真的以为在这里通过 location.href 获取到的 url 就是我们要进入的页面的 url,结果发现根本就不是的。在 router.afterEach 中通过 location.href 拿到的 url 是要跳出的页面的 url 而不是要进入的页面的 url,可以通过下面的代码来自己观察下拿到的 url 具体是哪个

router.afterEach((to, from) => {
  	alert('to-----------' + to.fullPath)
    alert('from-----------' + from.fullPath)
    alert('router-----------' + location.href)
})

在发现这个问题之后,我就去到页面组件的 mounted 生命周期方法里去拿 url,这样拿到的 url 就正确了,签名验证也通过了,可以正常分享了。到这里貌似整个问题都解决了,完美收工。然而作为一个有追求的码农,怎么能就这样结束呢。

在页面组件里拿 url ,固然可以拿到正确的 url,但是也会导致代码冗余,相同的代码要到处都写,非常不优雅。所以还是需要在 router.afterEach 里面作文章。这时候突然想到 url 可以通过域名加路由的方式拼接出来,这样貌似可以解决 router.afterEach 里面拿不到完整 url 的问题。代码就变成了下面这样:

router.afterEach((to, from) => {
 	alert('to-----------' + to.fullPath)
    alert('from-----------' + from.fullPath)
    alert('router-----------' + location.href)
    //微信分享前初始化 wxConfig
    wxShare.initConfig(location.origin + to.fullPath)
})

注意子域名

上面的代码中我们通过 location.origin + to.fullPath 域名加 path 拼接的方式来拿 url,一般情况下这样是可以拿到正确的 url 的,但是我这里就碰到了二般的情况。在将上面的代码打包测试后发现,特么的又报签名错误了,看来又是 url 出问题了,是不能用 location.origin + to.fullPath 这样的方式拼接 url 吗?

在经过各种打印调试后费了好长时间后才发现问题出在子域名上面,因为我这个程序是通过子域名访问的(这其实还是因为微信的限制,微信规定一个公众号下面只能配置三个域名白名单,多了不行,但是这三个主域名下面的子域名也可以正常使用,所以后台就给我配了个子域名)。

当使用了子域名时,需要把子域名也拼接上去,不然的话拼接出来的 url 肯定是不对的。比如我的子域名是 dc,那么我就需要这样拼

location.origin + '/dc' + to.fullPath

到这里,关于签名 url 的坑就踩完了,下面要做的就是对 js-sdk 进行封装,方便我们调用。

js-sdk 封装

要想优雅愉快的使用 js-sdk ,肯定要对它进行封装,封装也很简单,我就直接贴代码了。唯一有一点要注意的是,当 js-sdk 版本是 1.4.0 及以上时,分享给好友和朋友圈相应的方法名有变化,跟以前的版本不一样了。文档上说原来的方法即将被弃用(你倒是说清楚啥时候会被弃用啊),为了保险起见,我就将老方法和新方法都写上了。而且确实有人因为这二个方法没用对而分享失败。所以该怎么做,你们懂的。

特别注意:在 ios 中,只能用第一次进入应用时的 url 去请求签名才能验证成功,这是因为Vue项目在切换页面时,IOS中浏览器的url并不会改变,依旧是第一次进入页面的地址,所以需要将第一次进入应用的 url 存起来,当路由变化时还是使用第一次的 url 去请求签名。而 Android 不需要这样特殊处理,这也是微信分享的一个深坑

import wx from 'weixin-js-sdk'
import store from '../store'
import apiHelper from '../apis/apiHelper'

export default {
  initConfig (url) {
    if (!/micromessenger/i.test(navigator.userAgent)) {
      return
    }
    
    //如果是 iOS 设备,则使用第一次进入App时的 URL 去请求 wxConfig,不然的话会导致 iOS 中分享的链接不对
    if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
      //记录第一次进入时的链接,iOS 分享时需要用到
      if (!store.state.theFirstLink) {
        store.commit('setTheFirstLink', url)
      } else {
        url = store.state.theFirstLink
      }
    }
  
    // 将 url 传给后台请求微信签名配置
    apiHelper.purePost('/wechat/page_jssdk_config', {url: url}).then(data => {
      // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,
      if (data) {
        wx.config({
          debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
          appId: data.appId,   // 必填,公众号的唯一标识
          nonceStr: data.nonceStr,   // 必填,生成签名的随机串
          signature: data.signature, // 必填,签名,见附录1
          timestamp: data.timestamp, // 必填,生成签名的时间戳
          jsApiList: ["updateAppMessageShareData", "updateTimelineShareData", "onMenuShareAppMessage", "onMenuShareTimeline"], // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
        })
      }
    })
  },
  
  share (title, url, imgUrl, desc) {
    if (!/micromessenger/i.test(navigator.userAgent)) {
      return
    }
    let shareImg = '默认的分享图片地址'
    if (imgUrl) {
      shareImg = imgUrl
    }
    let shareDesc = '默认的分享文案'
    if (desc) {
      shareDesc = desc
    }
    
    wx.ready(() => {
      // 如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行
      // 在 weixin-js-sdk 1.4 之后,分享到朋友和朋友圈要分别使用 updateAppMessageShareData,updateTimelineShareData
      // 这里为了兼容老版本的微信,所以把以前的老版本的方法也写上
      wx.updateAppMessageShareData({
        title: title, // 分享标题
        desc: shareDesc, // 分享描述
        link: url, // 分享链接 默认以当前链接
        imgUrl: shareImg // 分享图标
      })
      wx.updateTimelineShareData({
        title: title,
        link: url,
        imgUrl: shareImg
      })
      wx.onMenuShareAppMessage({ // 分享给朋友,此方法即将被废弃,改用 updateAppMessageShareData
        title: title,
        desc: shareDesc,
        link: url,
        imgUrl: shareImg
      })
      wx.onMenuShareTimeline({ //分享到朋友圈,此方法即将被废弃,改用 updateTimelineShareData
        title: title,
        link: url,
        imgUrl: shareImg
      })
      wx.error(res => {
        // config信息验证失败会执行error函数,如签名过期导致验证失败,
        // 具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
        console.log(res)
      })
    })
  },
}

封装好之后,只需要将这个对象挂在 Vue 原型上,或者每次用之前导入一下,就可以很方便的调用了。

以上,就是这次做微信分享功能所经历的过程总结。其中的参考资料如下:
微信JS-SDK说明文档
Vue项目history模式下微信分享总结
微信分享JSSDK-invalid signature签名错误的解决方案
微信分享链接出现config:invalid signature错误的解决方法

你可能感兴趣的:(Vue,Vue,爬坑之旅)