场景
有个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