imwty

专注大前端Web应用开发实践

0%

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

前言

微信分享主要是可以把我们做的网页分享给好友或者分享到朋友圈,在发送给好友时,展示出来的消息不是一段很丑的网址,而是带着图文描述的特殊模板消息,很多传播性质比较强的网页都会借助这个特性提升传播性。要实现这个功能,需要我们接入微信的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方式引入呢?当然是可以的,安装命令如下:

1
2
3
4
// 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* 初始化设置
*/
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语法来实现配置成功后的操作啦!

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/** 设置微信分享 */
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文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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函数里调用该方法,即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

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

@/hooks/useWxShare.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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