微信外H5跳转小程序——组件(vue项目)
ztj100 2025-06-29 22:04 3 浏览 0 评论
场景
有个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
props | 类型 | 默认值 | 说明 |
path | string | 跳转的小程序路径 | |
queryData | object | {} | 携带的参数 |
ready | any | 只有当ready为true的时候,才会去执行最后一步 调用云函数,用于依赖一些异步数据的情况 | |
callFunctionName | string | 'openMiniapp' | 调用的云函数的名称 |
ghid | string | 小程序的原始id (gh开头的那个) | |
appid | string | 小程序的appid | |
envid | string | 云开发的云环境id |
附:
云环境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
}
},
...
}
复制代码
我设置的几个变量isPreConfigFinishisCanInitWechatisInitWechatFinish
· 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
相关推荐
- 使用Python编写Ping监测程序(python 测验)
-
Ping是一种常用的网络诊断工具,它可以测试两台计算机之间的连通性;如果您需要监测某个IP地址的连通情况,可以使用Python编写一个Ping监测程序;本文将介绍如何使用Python编写Ping监测程...
- 批量ping!有了这个小工具,python再也香不了一点
-
号主:老杨丨11年资深网络工程师,更多网工提升干货,请关注公众号:网络工程师俱乐部下午好,我的网工朋友。在咱们网工的日常工作中,经常需要检测多个IP地址的连通性。不知道你是否也有这样的经历:对着电脑屏...
- python之ping主机(python获取ping结果)
-
#coding=utf-8frompythonpingimportpingforiinrange(100,255):ip='192.168.1.'+...
- 网站安全提速秘籍!Nginx配置HTTPS+反向代理实战指南
-
太好了,你直接问到重点场景了:Nginx+HTTPS+反向代理,这个组合是现代Web架构中最常见的一种部署方式。咱们就从理论原理→实操配置→常见问题排查→高级玩法一层层剖开说,...
- Vue开发中使用iframe(vue 使用iframe)
-
内容:iframe全屏显示...
- Vue3项目实践-第五篇(改造登录页-Axios模拟请求数据)
-
本文将介绍以下内容:项目中的public目录和访问静态资源文件的方法使用json文件代替http模拟请求使用Axios直接访问json文件改造登录页,配合Axios进行登录请求,并...
- Vue基础四——Vue-router配置子路由
-
我们上节课初步了解Vue-router的初步知识,也学会了基本的跳转,那我们这节课学习一下子菜单的路由方式,也叫子路由。子路由的情况一般用在一个页面有他的基础模版,然后它下面的页面都隶属于这个模版,只...
- Vue3.0权限管理实现流程【实践】(vue权限管理系统教程)
-
作者:lxcan转发链接:https://segmentfault.com/a/1190000022431839一、整体思路...
- swiper在vue中正确的使用方法(vue中如何使用swiper)
-
swiper是网页中非常强大的一款轮播插件,说是轮播插件都不恰当,因为它能做的事情太多了,swiper在vue下也是能用的,需要依赖专门的vue-swiper插件,因为vue是没有操作dom的逻辑的,...
- Vue怎么实现权限管理?控制到按钮级别的权限怎么做?
-
在Vue项目中实现权限管理,尤其是控制到按钮级别的权限控制,通常包括以下几个方面:一、权限管理的层级划分...
- 【Vue3】保姆级毫无废话的进阶到实战教程 - 01
-
作为一个React、Vue双修选手,在Vue3逐渐稳定下来之后,是时候摸摸Vue3了。Vue3的变化不可谓不大,所以,本系列主要通过对Vue3中的一些BigChanges做...
- Vue3开发极简入门(13):编程式导航路由
-
前面几节文章,写的都是配置路由。但是在实际项目中,下面这种路由导航的写法才是最常用的:比如登录页面,服务端校验成功后,跳转至系统功能页面;通过浏览器输入URL直接进入系统功能页面后,读取本地存储的To...
- vue路由同页面重定向(vue路由重定向到外部url)
-
在Vue中,可以使用路由的重定向功能来实现同页面的重定向。首先,在路由配置文件(通常是`router/index.js`)中,定义一个新的路由,用于重定向到同一个页面。例如,我们可以定义一个名为`Re...
- 那个 Vue 的路由,路由是干什么用的?
-
在Vue里,路由就像“页面导航的指挥官”,专门负责管理页面(组件)的切换和显示逻辑。简单来说,它能让单页应用(SPA)像多页应用一样实现“不同URL对应不同页面”的效果,但整个过程不会刷新网页。一、路...
- Vue3项目投屏功能开发!(vue投票功能)
-
最近接了个大屏项目,产品想在不同的显示器上展示大屏项目不同的页面,做出来的效果图大概长这样...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)
- vmware17pro最新密钥 (34)
- mysql单表最大数据量 (35)