4

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

 2 years ago
source link: https://segmentfault.com/a/1190000040668215
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

发布于 9 月 11 日

微信分享主要是可以把我们做的网页分享给好友或者分享到朋友圈,在发送给好友时,展示出来的消息不是一段很丑的网址,而是带着图文描述的特殊模板消息,很多传播性质比较强的网页都会借助这个特性提升传播性。要实现这个功能,需要我们接入微信的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
需要体验的小伙伴,可以在微信中扫下边二维码
image.png
同时,笔者已经把项目的代码托管到了Github上,欢迎有需要的小伙伴点击该链接自取

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

最近工作稍微闲了些,笔者打算用Vue3+ts+vant从0开始完成一个公众号应用并将开发过程分享出来,也算是激励自己持续学习的一个方式吧。如果有想法和建议,欢迎找我探讨哦。
同时,为方便大家更好的探讨微信公众号开发相关技术,笔者也建了一个微信群,欢迎加入和大家一起学习成长。
image.png


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK