乐趣区

关于小程序:微信外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

退出移动版