Vue微信开发中微信分享的优雅实现

3,131 阅读7分钟

前言

微信分享主要是可以把我们做的网页分享给好友或者分享到朋友圈,在发送给好友时,展示出来的消息不是一段很丑的网址,而是带着图文描述的特殊模板消息,很多传播性质比较强的网页都会借助这个特性提升传播性。要实现这个功能,需要我们接入微信的JS-SDK,JS-SDK是什么呢?官方文档介绍如下:

微信JS-SDK是微信公众平台 面向网页开发者提供的基于微信内的网页开发工具包。 通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照、选图、语音、位置等手机系统的能力,同时可以直接使用微信分享、扫一扫、卡券、支付等微信特有的能力,为微信用户提供更优质的网页体验。

可以看到,JS-SDK能做很多事情,那么今天,我们先讨论关于微信分享的细节。如果你还需要实现微信授权登录相关的功能,那么可以查看笔者的这篇分享: Vue微信开发中授权登录的优雅实现

准备

第一步,不用多说,同样是先熟读一遍官方文档,文档地址如下:

微信开发JS-SDK使用说明文档

这里需要特殊说明的是,在开发之前,需要先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。 这个工作一定不能忘,切记切记。 使用JS-SDK还有一个关键的环节,那就是通过config接口注入权限验证配置,而配置中有个signature参数是需要借助服务端获取的,所以咱们开发时依然需要跪舔一波后端伙伴给予支持(全栈的看官就当我没说)。 看完文档的看官们,应该都能梳理出来咱们接入微信分享的具体流程了,笔者梳理如下:

  1. 引入JS-SDK;
  2. 通过调用后端接口获取签名,签名算法见该页
  3. 调用wx.config方法注入相关配置;
  4. 调用相关的分享的api,传入对应的分享信息;

实现

这里笔者以实现一个微信漂流瓶功能为例,分享一下编码过程;技术栈采用的是Vue3+typescript,Vue2的开发者还请根据情况做适当调整。

引入JS-SDK

官方文档的描述是引入js-sdk文件,即通过script方式引入,但我们现在是Vue应用,那么能不能通过npm仓库安装通过ESModule方式引入呢?当然是可以的,安装命令如下:

// js版本
yarn add weixin-js-sdk
// ts版本
yarn add weixin-js-sdk-ts

笔者这里安装的是ts版本, 安装完后,package.json中显示了安装的版本 image.png

封装模块

本着功能解耦原则和方便复用,笔者决定单独新建一个文件,专门封装js-sdk相关的功能。在vue3中,当然是封装成hook啦。所以,咱们在hooks目录下,新建了一个useWxSDK.ts文件; 然后,开始封装第一个方法,即wx.config

/**
 * 初始化设置
 */
function initConfig(configInfo) {
  return new Promise((resolve) => {
    wx.config({
      debug: false,
      appId: configInfo.appId,
      timestamp: configInfo.timestamp,
      nonceStr: configInfo.nonceStr,
      signature: configInfo.signature,
      jsApiList: configInfo.jsApiList ?? [
        'chooseImage',
        'uploadImage',
        'previewImage',
        'onMenuShareTimeline',
        'onMenuShareAppMessage',
        'chooseWXPay',
      ],
      openTagList: [],
    })
    wx.ready(() => {
      resolve(true)
    })
  })
}

这里说明一下wx.config方法,为啥要封装为Promise呢?其实可以看到JS-SDK的API设计是比较原始的,我们需要通过wx.ready去注册配置成功后的回调函数,笔者在这里用Promise封装,在ready回调函数里调用Promise.resolve,那么我们在用的时候,就可以通过优雅的then语法来实现配置成功后的操作啦!

接下来封装分享好友和微信朋友圈的方法:

/** 设置微信分享 */
function setShareInfo(shareInfo,onSuccess, onCancel) {
  wx.onMenuShareTimeline({
    title: shareInfo.title, // 分享标题
    link: shareInfo.link, // 分享链接,可以不是当前页面,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
    imgUrl: shareInfo.imgUrl,
    success: function () {
      // 用户确认分享后执行的回调函数
      onSuccess()
    },
    cancel: function () {
      onCancel()
      // 用户取消分享后执行的回调函数
    },
  })
  wx.onMenuShareAppMessage({
    title: shareInfo.title, // 分享标题
    desc: shareInfo.desc,
    link: shareInfo.link, // 分享链接,可以不是当前页面,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
    imgUrl: shareInfo.imgUrl,
    type: 'link', // 分享类型,music、video或link,不填默认为link
    success: function () {
      // 用户确认分享后执行的回调函数
      onSuccess()
    },
    cancel: function () {
      // 用户取消分享后执行的回调函数
      onCancel()
    },
  })
}

接下来,开始搭积木。因为考虑到分享这个操作,也是会在多个页面复用,那么有必要再抽象一个分享的方法,这样在需要用到微信分享的地方直接调用该方法就行。因此,笔者又新建了一个useWxShare.ts文件

import { getJsSDKConfigApi } from "@/api/wechat";
import { useWxSDK } from "@/hooks/useWxSDK";

export function useWxShare(shareConfig: {
  title: string;
  imgUrl: string;
  desc: string;
}) {
  const { initConfig, setShareInfo } = useWxSDK();

  const shareUrl = window.location.href.split("#")[0];

  getJsSDKConfigApi(shareUrl).then((config) => {
    // 调用后端接口获取config相关信息
    initConfig(config).then(() => {
      // 注入wx.config成功后,设置微信分享相关
      setShareInfo({
        ...shareConfig,
        link: shareUrl,
      });
    });
  });
}

至此,封装完毕,然后,咱们到页面组件到mounted函数里调用该方法,即可

<script>
import { useWxShare } from '@/hooks/useWxShare'

export default defineComponent({
  setup() {
  },
  mounted() {
    useWxShare({
      title: '这是标题',
      desc: '这是描述',
      imgUrl: 'http://yourimg.com/share-pic.png',
    })
  },
})
</script>

补坑

这样就结束了吗?当然不是,这里边有个大坑,笔者在之前的博客中也分享过,就是如果我们的页面是采用的Vue Router的history模式的路由的话,在某些情况下,会出现iOS设备上分享失效的问题。举个栗子。假设我们都通过http://domain.com 进入,然后跳转到路由为/share的页面需要用到jssdk,那么实际js-sdk进行签名校验时所获取的当前页面url在ios和andrioid是不同的,在安卓上没有任何问题,js-sdk校验的url就是当前页面的url,也就是http://domain.com/share。而在iOS上,js-sdk校验的url是我们刚进入页面时候的url,也就是http://domain.com,然而我们后端那边签名用的是当前页面的url,这就导致iOS上,签名校验不成功。 那么该如何处理呢?笔者这边采取的办法是在入口文件或者根组件中记录当前页面URL,在页面组件创建完成后,ios获取记录的url进行签名,android获取当前路由。所以填坑如下:

App.vue

import { defineComponent } from 'vue'
import { useWxSDK } from '@/common/hooks/useWxSDK'
import { commonStore } from '@/store/modules/common'

export default defineComponent({
  name: 'App',
  setup() {
    const { isiOSWechat } = useWxSDK()
    // 检测到是ios微信,则把入口页地址记录到store中
    if (isiOSWechat()) {
      const url = window.location.href.split('#')[0]
      commonStore.saveVisitUrl(url)
    }
  },
})
</script>

@/hooks/useWxSDK.ts

  /** 是否是ios微信 */
  function isiOSWechat() {
    return (window as any).__wxjs_is_wkwebview
  }

@/hooks/useWxShare.ts

import { getJsSDKConfigApi } from '@/api/wechat'
import { useWxSDK } from '@/common/hooks/useWxSDK'
import { commonStore } from '@/store/modules/common'

export function useWxShare(shareConfig: { title: string; imgUrl: string; desc: string }) {
  const { initConfig, setShareInfo, isiOSWechat } = useWxSDK()

  const shareUrl = window.location.href.split('#')[0]
  // 对签名url做特殊判断处理
  const signatureUrl = isiOSWechat() ? commonStore.commonState.visitUrl : shareUrl

  getJsSDKConfigApi(signatureUrl).then((config) => {
    initConfig(config).then(() => {
      setShareInfo({
        ...shareConfig,
        link: shareUrl,
      })
    })
  })
}

@/store/modules/common.ts

import { Module, VuexModule, Mutation, getModule } from 'vuex-module-decorators'
import store from '@/store'
import { initialUnencryptedStorage } from '../globals'

interface CommonState {
  /** ios微信用,记录访问时候页面url */
  visitUrl: string
}

const NAME = 'common'

@Module({
  namespaced: true,
  name: NAME,
  dynamic: true,
  store,
  preserveState: Boolean(initialUnencryptedStorage[NAME]),
})
export class Common extends VuexModule {
  commonState: CommonState = {
    visitUrl: '',
  }

  @Mutation
  saveVisitUrl(url: string): void {
    this.commonState.visitUrl = url
  }
}

export const commonStore = getModule<Common>(Common)

看效果

首先咱们还是使用微信开发工具上看看效果 先,输入咱项目的地址 image.png 打开后,可以看到,获取config信息的请求已成功发送和返回,再看控制台 image.png 控制台显示了wx.config的配置日志以及设置分享的日志,这个表示我们的分享配置成功了。点右上角分享,如果能显示我们配置的分享标题,那就没问题啦。

image.png

最后,还是要在真机上试试看,真机上分享出来后,效果如下:

image.png

同时,笔者已经把项目的代码托管到了github上,欢迎有需要的小伙伴点击该链接自取

开发遇到问题怎么办?别着急,点击 这个链接或者关注公众号【过目笔记】,就可以同时拥有包括ChatGPT在内的多个AI大模型助手帮你排忧解难!

总结

其实微信分享相对来说,功能并不复杂,难点在于如何妥当的处理JS-SDK在单页应用上的一些坑。另外,也就是代码组织和封装的问题了,笔者看过很多项目,在代码复用和抽象上没有多花心思,导致整个项目到处都是复制粘贴的重复代码,显得很臃肿。经过笔者这样解耦抽象后,在调用层只需要简单的一个函数即可,是不是就显得格外的优雅呢?