微信外H5跳转小程序——<JumpApp/>组件(vue项目)

场景
有个H5(vue项目),需要实现点击商品item跳转到小程序,微信内和微信外都要支持,这里我们只介绍一下H5在微信外的跳转。

如图所示,红框内是一个商品,就是点击这里,要跳转小程序:


配置微信小程序云开发(云函数)
1、开通云开发

然后选择免费额度

2、云开发权限设置

找到权限设置,把这里的「未登录用户访问权限」点开

3、新建云函数openMiniapp

这里我们先只需要建个名为openMiniapp的云函数放在这里就行,它的代码后面再写。

4、修改云函数权限


添加一下这部分配置,注意这里的名称要和云函数的名称一致:

云函数代码
1、编写云函数代码
如果是原生小程序,当配置完云开发+云函数之后,小程序项目目录应该就多出一个云函数的目录(可能叫cloudbase,但是因为我这里是用的uniapp,这个目录是自定义的,我设置为wxcloudfunctions):

附:
uniapp配置云函数教程1
uniapp配置云函数教程2

云函数的代码:

package.json:

{

"name": "GENERAL",

"version": "1.0.0",

"description": "",

"main": "index.js",

"scripts": {

"test": "echo \"Error: no test specified\" && exit 1"

},

"author": "",

"license": "ISC",

"dependencies": {

"wx-server-sdk": "~2.3.2"

}

}

复制代码

index.js:

const cloud = require('wx-server-sdk')

cloud.init()

exports.main = async (event, context) => {

const { path = '', queryData = {}, } = event // 我从H5那边传进来的参数,可以从event里获取到

// 获取queryStr

let queryStrArr = []

for (let key in queryData) {

const str = `${key}=${queryData[key]}` // name=tom

queryStrArr.push(str)

}

const queryStr = queryStrArr.join('&')

console.log('path', path)

console.log('queryStr', queryStr)

return cloud.openapi.urlscheme.generate({

jumpWxa: {

path: path ? ('/' + path) : '', // 打开小程序时访问路径,为空则会进入主页

query: queryStr, // 可以使用 event 传入的数据制作特定参数,无需求则为空

},

isExpire: true, //是否到期失效,如果为true需要填写到期时间,默认false

expire_time: Math.round(new Date().getTime() / 1000) + 3600

//我们设置为当前时间3600秒后,也就是1小时后失效

//无需求可以去掉这两个参数(isExpire,expire_time)

})

}

复制代码

2、部署云函数
右键,选择创建并部署。

这样云函数的部署就完成了。

H5部分
1、<JumpApp/>
我的想法是写一个通用组件<JumpApp/>,不包含任何样式,内容通过<slot/>传进来,这样后面不管什么样的跳转都可以使用它了。

我可以这样:

<!-- 商品 -->

<view v-for="item in goodsList ?? []" :key="item.id" class="item_wrap">

<JumpApp

:ghid="jumpAppGhCode"

:appid="jumpAppAppid"

:envid="jumpAppEnvid"

:ready="doctorInfo?.doctorCode"

:path="`pages/product/details/details`"

:queryData="{

dCode: doctorInfo?.developerCode ?? '',

skuCode: item.chnlGoodsId,

}"

>

<view class="service_package_item">

<image class="service_package_item_icon" :src="item.goodsImg?.split(',')[0] ?? ''"></image>

<view class="service_package_item_content">

<view class="service_package_item_content_title_wrap">

<view class="service_package_item_content_title">{{ item.goodsName }}</view>

<view class="service_package_item_content_price">{{ item.goodsFeeStr }}元</view>

</view>

<view class="service_package_item_content_desc">{{ item.goodsDesc }}</view>

</view>

</view>

</JumpApp>

</view>

复制代码

也可以这样:

<view class="buy_btn_box">

<customButton v-if="drugListInfo?.disabled == 1" name="清单已失效,请重新联系医生" :disabled="true" />

<!-- 跳转小程序 -->

<JumpApp

v-else

:ghid="jumpAppGhCode"

:appid="jumpAppAppid"

:envid="jumpAppEnvid"

:ready="jumpInfo.path"

:path="jumpInfo.path"

:queryData="jumpInfo.queryData"

>

<customButton type="primary" name="立即购买 送药上门" />

</JumpApp>

</view>

复制代码

2、介绍props

附:
云环境id的位置:

3、关键代码
配置H5的云函数 init

methods: {

...

// 先配置

async preConfig() {

const self = this

if (isWeixin()) {

// 这里先忽略,这是 微信内H5 配置wxjssdk的部分

return await configWx([], ['wx-open-launch-weapp'])

} else {

// 微信外

if (!window.tcb) {

window.tcb = new window.cloud.Cloud({

identityless: true,

resourceAppid: self.appid,

resourceEnv: self.envid,

})

}

return await window.tcb.init()

}

},

...

}

复制代码

window.jumpAppState
因为有时一个页面里,可能会有多个<JumpApp/>,比如一个需要跳转小程序的商品列表,每个商品item都要包裹一个<JumpApp/>

而云函数其实只需要初始化一次,因为云函数是挂到window上的。即使是微信内的H5,每个页面wx jssdk也只需要初始化一次。

所以这里增加了个window.jumpAppState的变量用来判断当前页面是否正在初始化和是否初始化完成,用window.location.hash作为key,用来区分不同的页面。

 async mounted() {

console.log('jumpApp mounted')

if (!window.jumpAppState) window.jumpAppState = {}

// console.log(window.jumpAppState[`isReady_${window.location.hash}`])

// console.log(window.jumpAppState[`isGettingReady_${window.location.hash}`])

// 先配置

if (!window.jumpAppState[`isReady_${window.location.hash}`] && !window.jumpAppState[`isGettingReady_${window.location.hash}`]) {

console.log('进入配置')

window.jumpAppState[`isGettingReady_${window.location.hash}`] = true

await this.preConfig()

window.jumpAppState[`isGettingReady_${window.location.hash}`] = false

window.jumpAppState[`isReady_${window.location.hash}`]

}

// 先配置 end

console.log('配置完毕')

this.isPreConfigFinish = true

},

复制代码

调用云函数openMiniapp,拿到openLink

methods: {

...

// 微信外

async initOutWeixin() {

const self = this

let res

try {

res = await window.tcb.callFunction({

name: self.callFunctionName, // 提供UrlScheme服务的云函数名称

data: {

path: self.path,

queryData: self.queryData,

},

})

} catch (e) {

console.log('云函数失败了', e)

}

console.log('云函数结果', res)

this.minihref = res?.result?.openlink ?? ''

this.isInitWechatFinish = true

},

// 微信外跳转小程序 click

handleOutWeixinClick() {

if (!isWeixin() && this.minihref && this.isInitWechatFinish) {

window.location.href = this.minihref

}

},

...

}

复制代码

我设置的几个变量 isPreConfigFinish isCanInitWechat isInitWechatFinish
· isPreConfigFinish
代表window.tcb是否init完成(如果是微信内的话,代表wx jssdk是否config完成)

· isCanInitWechat
是否preConfig完成,同时外部的一些参数是否准备好了。
因为有时我们跳转时携带的参数是异步获取的,比如订单号、商品code等,所以设置了一个props.ready的变量。
只有当isPreConfigFinish已经为true,且一些异步的数据已经拿到即props.ready为true的时候,isCanInitWechat为true

· isInitWechatFinish
代表云函数调用成功了,拿到了openLink(微信内的话,就是wxjssdk config成功,并且<wx-open-launch-weapp/>已经加到了html里)

最终拿到的这个minihref就是类似这种的地址:"weixin://dl/business/?ticket=slejlsdjlf",我们直接调用window.location.href = this.minihref 就能触发跳转小程序了。

ok 这样就开发完成了
完整代码

<JumpApp/>

<template>

<view class="p_1646876870010">

<div class="open_app_div" @click="handleOutWeixinClick" v-html="openWeappBtnHtml"></div>

<slot></slot>

</view>

</template>

<script>

import { ref, onMounted } from 'vue'

import { configWx } from '@/services/wx'

import { jumpAppGhCode } from '@/utils/consts'

import { isWeixin } from '@/utils/utils_h5'

// 因为一个页面可能有多个 JumpApp组件,用window 保存是否配置成功的状态,并且用当前页面的hash值区分 window.location.hash

// window.jumpAppState = {

// [`isGettingReady_${window.location.hash}`]: false,

// [`isReady_${window.location.hash}`]: false,

// }

/**

* 顺序: (几个变量)

* 1、preConfig

* 2、外部的ready 可以了

*

* 注:1、2 完成之后 isCanInitWechat 变为true ,开始init微信小程序的跳转

*

* 3、isInitWechatFinish 最终成功了

*/

export default {

data() {

return {

isPreConfigFinish: false, // preConfig是否完成

isInitWechatFinish: false, // 最终成功了

// 微信内

openWeappBtnHtml: '',

// 微信外

minihref: '',

}

},

props: {

// path 不需要带 '/'

path: {

type: String,

},

queryData: {

type: Object,

default: () => ({}),

},

ready: {}, // 其它的外部的数据是否准备好 ready 为true的时候 才 添加wx-open-launch-weapp 或者 触发云函数

// 调用的云函数名称

callFunctionName: {

type: String,

default: 'openMiniapp',

},

/**

* 微信内

*/

ghid: {

type: String,

},

/**

* 微信外

*/

appid: {

type: String,

},

// 云环境id

envid: {

type: String,

},

},

computed: {

isCanInitWechat() {

return this.isPreConfigFinish && this.ready

},

},

watch: {

isCanInitWechat(v) {

if (v) {

setTimeout(() => {

if (isWeixin()) {

this.initInWeixin()

} else {

this.initOutWeixin()

}

}, 0)

}

},

},

async mounted() {

console.log('jumpApp mounted')

if (!window.jumpAppState) window.jumpAppState = {}

// console.log(window.jumpAppState[`isReady_${window.location.hash}`])

// console.log(window.jumpAppState[`isGettingReady_${window.location.hash}`])

// 先配置

if (!window.jumpAppState[`isReady_${window.location.hash}`] && !window.jumpAppState[`isGettingReady_${window.location.hash}`]) {

console.log('进入配置')

window.jumpAppState[`isGettingReady_${window.location.hash}`] = true

await this.preConfig()

window.jumpAppState[`isGettingReady_${window.location.hash}`] = false

window.jumpAppState[`isReady_${window.location.hash}`]

}

// 先配置 end

console.log('配置完毕')

this.isPreConfigFinish = true

},

methods: {

// 先配置

async preConfig() {

const self = this

if (isWeixin()) {

return await configWx([], ['wx-open-launch-weapp'])

} else {

if (!window.tcb) {

window.tcb = new window.cloud.Cloud({

identityless: true,

resourceAppid: self.appid,

resourceEnv: self.envid,

})

}

return await window.tcb.init()

}

},

// 微信内

async initInWeixin() {

console.log('微信内')

// 获取queryStr

let queryStrArr = []

for (let key in this.queryData) {

const str = `${key}=${this.queryData[key]}` // name=tom

queryStrArr.push(str)

}

const queryStr = queryStrArr.join('&')

const jumpPath = `${this.path}?${queryStr}`

this.openWeappBtnHtml = `

<wx-open-launch-weapp

id="launch-btn"

username="${this.ghid}"

path="${jumpPath}"

style="background-color:transparent;"

>

<template>

<div style="width:800px;padding:100px;color:transparent;background-color:transparent;font-size:14px">跳转</div>

</template>

</wx-open-launch-weapp>

`

this.isInitWechatFinish = true

},

// 微信外

async initOutWeixin() {

const self = this

let res

try {

res = await window.tcb.callFunction({

name: self.callFunctionName, // 提供UrlScheme服务的云函数名称

data: {

path: self.path,

queryData: self.queryData,

},

})

} catch (e) {

console.log('云函数失败了', e)

}

console.log('云函数结果', res)

this.minihref = res?.result?.openlink ?? ''

this.isInitWechatFinish = true

},

// 微信外跳转小程序 click

handleOutWeixinClick() {

if (!isWeixin() && this.minihref && this.isInitWechatFinish) {

window.location.href = this.minihref

}

},

}, // methods end

}

</script>

<style lang="less">

.p_1646876870010 {

position: relative;

overflow: hidden;

.open_app_div {

position: absolute;

top: 0;

bottom: 0;

left: 0;

right: 0;

}

}

</style>

复制代码

附上微信内H5配置wxjssdk的代码configWx

/**

* @param {Array<string>} jsApiArr

* @returns

*/

export function configWx(jsApiArr = [], openTagArr = []) {

return new Promise(async (resolve, reject) => {

if (!wx) return reject()

wx.ready(function (res) {

// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。

console.log('wx ready 我打印的', res)

return resolve()

})

wx.error(function (err) {

// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。

console.log('wx error 我打印的', err)

return reject(err)

})

// 请求

let config = null

try {

/**

* 苹果分享会是调取签名失败是因为:苹果在微信中浏览器机制和安卓不同,有IOS缓存问题,和IOS对单页面的优化问题,

* 通俗点说安卓进行页面跳转分享时会刷新当前的url,而苹果不会,苹果是通过历史记录进来的,不会刷新url所以会导致签名失败)。

*

* 所以

* 获取signUrl 安卓传全部的href,ios只用传#之前的

*/

let signUrl = ''

if (isIOS()) {

signUrl = window.location.href.split('#')[0]

} else {

signUrl = window.location.href

}

const res = await request({

url: '/h5/user/jsapi/initConfig',

data: { url: signUrl },

})

config = res?.data ?? {}

} catch (error) {

return reject(error)

}

if (config) {

wx.config({

// debug: getIsProd() ? false : true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。

debug: false,

appId: appid + '',

timestamp: config.timestamp + '', // 必填,生成签名的时间戳

nonceStr: config.nonceStr + '', // 必填,生成签名的随机串

signature: config.signature + '', // 必填,签名

jsApiList: ['hideMenuItems', ...jsApiArr], // 必填,需要使用的JS接口列表 // 这个貌似不能空

openTagList: [...openTagArr],

})

}

})

}

最后
如果你觉得此文对你有一丁点帮助,点个赞。或者可以加入我的开发交流群:1025263163相互学习,我们会有专业的技术答疑解惑

如果你觉得这篇文章对你有点用的话,麻烦请给我们的开源项目点点star:http://github.crmeb.net/u/defu不胜感激 !

PHP学习手册:https://doc.crmeb.com
技术交流论坛:https://q.crmeb.com

以上是 微信外H5跳转小程序——&lt;JumpApp/&gt;组件(vue项目) 的全部内容, 来源链接: utcz.com/z/267830.html

回到顶部