其实微信小程序就是js+css,不过腾讯封装过了。如果前端有经验,微信小程序很容易掌握。我的头条里自己有几篇普及微信小程序的文章,希望对你有帮助。
有个H5(vue项目),需要实现点击商品item跳转到小程序,微信内和微信外都要支持,这里我们只介绍一下H5在微信外的跳转。
如图所示,红框内是一个商品,就是点击这里,要跳转小程序:
配置微信小程序云开发(云函数)1、开通云开发
然后选择免费额度
找到权限设置,把这里的「未登录用户访问权限」点开
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的位置:
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就是类似这种的地址:"?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:不胜感激 !
PHP学习手册:
技术交流论坛:
微信开放社区有一个长青帖「微信 H5 页面可以直接跳转到小程序吗?」
这个帖子 2018 年 7 月 9 日建立,在发帖当天得到官方工作人员回复「H5 不支持跳转到小程序」后,发帖人记录下了工作人员回复时间,尚未支持该功能。之后,诸多来此帖询问同样功能的开发者也都会在问题后面加上日期「请问现在可以了吗,2020 年 X 月 X 日?」
▲ 打卡等回复
对这个问题好奇的人一直都有,且很多。但直到今年 8 月,诸多提问者才收到了一个不同的回答:「微信内网页才可以。」
今年 7 月,H5 跳转小程序的能力公测,开发者只需要在 H5 内使用「微信开放标签」,用户点击标签就能跳转至小程序。
虽然需要在微信内置的网页中才能打开小程序,但对于期待此功能已久的开发者来说,这个功能已经很够用了,还有开发者用了一些小手段,让这个功能更无门槛,也更好偷懒。
就比如我们最近发现的一个应用案例,开发者就模仿了微信小程序分享朋友圈的样式使用了微信开放标签的能力。唯一不同的是,朋友圈打开小程序在点击开放标签后即可直接打开小程序,H5 则是在点击开放标签后还需要由用户确认后才能真正前往小程序。
和朋友圈打开小程序用同样的页面设计样式,这不只是合规「偷懒」,在用户认知上也更好培养用户的使用习惯。这样的小心机,其实非常好实现。
想要实现这样的跳转效果,只需要在页面中添加一个特殊的标签:
<wx-open-launch-weapp> //小程序跳转按钮
具体的代码实现:
<wx-open-launch-weapp id="launch-btn" username="gh_****" <!-- 所需跳转的小程序原始 id,即小程序对应的以 gh_ 开头的 id --> path="/pages/xxx"> <!-- 所需跳转的小程序内页面路径及参数,所声明的页面路径必须添加.html 后缀,如 pages/home/index.html --> <template> <button class="btn"> 前往小程序</button> </template></wx-open-launch-weapp>
将这段代码加入到页面中,就有了一个可跳转指定小程序页面的按钮。使用此标签后,用户需在网页内点击标签按钮方可跳转小程序。通过该开放标签打开小程序的场景值为 1167,可用于数据埋点统计。
唯一需要注意的是此功能的开通需要以下条件(二选一):
已认证的服务号,服务号绑定 “JS 接口安全域名” 下的网页可使用此标签跳转任意合法合规的小程序。已认证的非个人主体的小程序,使用小程序云开发的静态网站托管绑定的域名下的网页,可以使用此标签跳转任意合法合规的小程序。详细操作步骤请查看开发文档。
由于该能力可以跳转到任意合法合规的小程序,因此应用场景较为广阔。
对聚合型平台来说,这个能力可以让他们的业务范围更灵活。就以内容推荐聚合的旅游种草平台为例,在分享游记行程时,就可以通过跳转小程序的方式进行商业合作。分享机票花销的时候可以跳航空公司的小程序,分享行程民宿时也有 OTA 平台相关的小程序。
同样的分享形式公众号文章也能做,但 H5 会比文章更灵活,更便于调整。
而 H5 跳转合法合规小程序无限制的特性倒也证明了它有更多的发挥空间。虽然小程序跳转小程序 500 个对大部分开发者来说已经够用了,但对有更多业务需求的开发者来说,目前基本无限制的 H5 跳转或许更适合有特定需求的开发者。
但需要注意的是,这个功能还是有界限的。今年小程序跳转小程序放宽限制的时候,微信还特意指出「平台将坚决打击小程序盒子等互推行为」,所以这个功能对于盒子类的小程序来说也并不是什么政策利好,平台对小程序盒子的态度依旧是打击的。
目前看到的 H5 跳转小程序的案例多为内容类,你觉得这还有些什么可能性呢?
使用官方文档web-view组件:web-view | 微信开放文档
1、首先得通过微信公众平台配置业务域名,配置业务域名需要将校验文件上传到网站服务器根目录下。
步骤一:登录公众平台找到开发管理
步骤二:找到业务域名新增,下载校验文件,将校验文件放到网站服务器根目录下。
步骤三:浏览器可访问到网站服务器根目录下的校验文件,即可保存业务域名成功。
备注:微信小程序跳转到第三方网页需要获取第三方网站的同意,想要跳转到别人的网站,得获取别人的同意,吧把校验文件让别人放到网站服务器根目录下,想想别人怎么可能随便同意,搞笑。。。
这里以跳到自己开发的h5页面为例。
2、在小程序代码中加入新页面并配置路由,新页面使用web-view承载网页的容器。会自动铺满整个小程序页面
在app.json配置路由文件
"pages": [ ...... "pages/out/out" //新增页面 ],
out/out下的index.wxml
<web-view src=""></web-view>//你没有看错,就是这么一行,把你要跳转的界面用src就可以了,以后要跳转到哪,src里面地址就写哪!
3、在想要跳转的时候使用路由跳转方法去跳转就行。
wx.navigateTo({ url:'/pages/out/out', // success:function() { }, //成功后的回调; fail:function() { }, //失败后的回调; complete:function() { } //结束后的回调(成功,失败都会执行) })
总结:我遇到的需求是在微信小程序跳转进入第三方H5支付页面进行农行支付,直接跳到农行的支付页面是不可能的,人家怎么可能同意放校验文件在他们服务器上,所以正在尝试跳到自己的h5页面,再在h5页面使用iframe去内嵌第三方支付页面,之后再同大家分享。
后续:h5内嵌iframe/或者通过a标签还是不行,需要走校验流程,以下是调试关闭校验业务域名设置的调试结果,发布到线上依然实现不了
iframe:
a标签