scenario

There is an H5 (VUE project), which needs to realize the jump to the small program by clicking the item. It should be supported both inside and outside wechat. Here we only introduce the jump of H5 outside wechat.

As shown in the picture, inside the red box is a product, that is, click here to jump to the applet:

Configure wechat small program cloud development (cloud function)

1. Open cloud development



Then choose the free amount

2. Cloud development permission setting

Go to the permissions Settings and turn on “user access permissions” here

3. Create a cloud functionopenMiniapp

So here we’re just going to call itopenMiniappThe cloud function is put here, its code will be written later.

4. Modify cloud function permissions

Add this configuration and make sure the name is the same as the name of the cloud function:

Cloud function code

1. Write cloud function code

For native applets, after cloud development + cloudfunctions are configured, the applets project directory should have an additional directory for cloudfunctions (probably called Cloudbase, but since I am using uniapp, this directory is custom, I set it to wxCloudFunctions) :

Attached: UniAPP configuration cloud function tutorial 1 UniAPP configuration cloud function tutorial 2

Cloud function code:

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." "}}Copy the code

Index. Js:

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

exports.main = async (event, context) => {
	const { path = ' ', queryData = {}, } = event // The parameters I passed in from H5 can be obtained from event

	/ / get 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) : ' '.// Open the small program access path, empty will enter the home page
			query: queryStr, // You can use the data passed in by event to make specific parameters, null if not required
		},
		isExpire: true.If the value is true and the expiration time is required, the default value is false
		expire_time: Math.round(new Date().getTime() / 1000) + 3600
		// We set the current time to 3600 seconds, that is, 1 hour after expiration
		// There is no need to remove these parameters (isExpire, expire_time)})}Copy the code

2. Deploy cloud functions

Right-click and select Create and Deploy.

This completes the deployment of the cloud functions.

H5 part

1.<JumpApp/>

My idea is to write a generic component,
, that contains no styles and is passed in through
so that any subsequent jumps can use it.

I can do this:

<! - goods -- -- >
<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>
Copy the code

It could also go like this:

<view class="buy_btn_box">
    <customButton v-if="drugListInfo? .disabled == 1" name="List is no longer valid. Please contact the doctor again." :disabled="true" />
    <! -- Jump applet -->
    <JumpApp
      v-else
      :ghid="jumpAppGhCode"
      :appid="jumpAppAppid"
      :envid="jumpAppEnvid"
      :ready="jumpInfo.path"
      :path="jumpInfo.path"
      :queryData="jumpInfo.queryData"
    >
      <customButton type="primary" name="Buy now, deliver medicine to your door." />
    </JumpApp>
  </view>
Copy the code

2. Introduce props

props type The default value instructions
path string Jump to the applet path
queryData object {} Carrying parameters
ready any The last call to the cloud function is performed only when ready is true, for cases that depend on some asynchronous data
callFunctionName string ‘openMiniapp’ The name of the called cloud function
ghid string The original ID of the applet (the one starting with GH)
appid string Appid of the applet
envid string Cloud environment ID for cloud development

Attachment: Location of cloud environment ID:

3. Key code

Configure H5’s cloud function init

  methods: {
    ...
    / / configuration first
    async preConfig() {
      const self = this
      if (isWeixin()) {
        // This is the part of wechat H5 configuration WXJSSDK
        return await configWx([], ['wx-open-launch-weapp'])}else {
        / / WeChat outside
        if (!window.tcb) {
          window.tcb = new window.cloud.Cloud({
            identityless: true.resourceAppid: self.appid,
            resourceEnv: self.envid,
          })
        }
        return await window.tcb.init()
      }
    },
  ...
  }
Copy the code

window.jumpAppState

Because sometimes there may be multiple
in a single page, such as a list of items that need to jump to the applet, each item must be wrapped in a

The cloud function actually only needs to be initialized once, because the cloud function is attached to the window. Even for H5 within wechat, the WX JSSDK only needs to be initialized once per page.

The variable window.jumpappState is used to determine whether the current page is being initialized and whether the initialization is complete. Window.location. hash is used as the key to distinguish between different pages.

  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}`])
    / / configuration first
    if (!window.jumpAppState[`isReady_The ${window.location.hash}`] &&!window.jumpAppState[`isGettingReady_The ${window.location.hash}`]) {
      console.log('Enter configuration')
      window.jumpAppState[`isGettingReady_The ${window.location.hash}`] = true
      await this.preConfig()
      window.jumpAppState[`isGettingReady_The ${window.location.hash}`] = false
      window.jumpAppState[`isReady_The ${window.location.hash}`]}// Configure end first

    console.log('Configured')

    this.isPreConfigFinish = true
  },
Copy the code

Call the cloud function openMiniapp and get the openLink

methods: {
    ...
    / / WeChat outside
    async initOutWeixin() {
      const self = this
      let res
      try {
        res = await window.tcb.callFunction({
          name: self.callFunctionName, // Provide the cloud function name of the UrlScheme service
          data: {
            path: self.path,
            queryData: self.queryData,
          }, 
        })
      } catch (e) {
        console.log('The cloud function failed', e)
      }
      console.log('Cloud function result', res)
      this.minihref = res? .result? .openlink ??' '
      this.isInitWechatFinish = true
    },
    // Click
    handleOutWeixinClick() {
      if(! isWeixin() &&this.minihref && this.isInitWechatFinish) {
        window.location.href = this.minihref
      }
    },
    ...
}
Copy the code

A couple of variables that I setisPreConfigFinish isCanInitWechat isInitWechatFinish

· isPreConfigFinish indicates whether window. TCB init is complete (if it is in wechat, it indicates whether WX JSSDK config is complete)

· isCanInitWechat is preConfig complete and some external parameters are ready. Because sometimes we jump with parameters that are retrieved asynchronously, such as order number, product code, etc., we set a props. Ready variable. IsCanInitWechat is true only if isPreConfigFinish is true and some asynchronous data is being held (props. Ready)

· isInitWechatFinish means that the cloud function is successfully invoked and openLink is available.

The resulting minihref is something like this: “weixin://dl/business/? Ticket =slejlsdjlf”, we call window.location.href = this.minihref to trigger the jump applet.

Ok Development is complete

The complete code

<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'

// Since a page can have multiple JumpApp components, use window to keep the status of whether the configuration was successful, and use the current page hash value to distinguish window.location.hash
// window.jumpAppState = {
// [`isGettingReady_${window.location.hash}`]: false,
// [`isReady_${window.location.hash}`]: false,
// }

/** * order: (several variables) * 1, preConfig * 2, external ready 1, isCanInitWechat change to true, init wechat applet jump * * 3, isInitWechatFinish finally successful */

export default {
  data() {
    return {
      isPreConfigFinish: false.// Whether preConfig is complete
      isInitWechatFinish: false.// Finally succeeded

      / / WeChat inside
      openWeappBtnHtml: ' './ / WeChat outside
      minihref: ' ',}},props: {
    // Path does not need '/'
    path: {
      type: String,},queryData: {
      type: Object.default: () = >({})},ready: {}, // Do not add WX-open-launch-syndrome or trigger cloud functions until other external data is ready for true
    // The name of the called cloud function
    callFunctionName: {
      type: String.default: 'openMiniapp',},/**
     * 微信内
     */
    ghid: {
      type: String,},/** * wechat */
    appid: {
      type: String,},// Cloud environment 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}`])
    / / configuration first
    if (!window.jumpAppState[`isReady_The ${window.location.hash}`] &&!window.jumpAppState[`isGettingReady_The ${window.location.hash}`]) {
      console.log('Enter configuration')
      window.jumpAppState[`isGettingReady_The ${window.location.hash}`] = true
      await this.preConfig()
      window.jumpAppState[`isGettingReady_The ${window.location.hash}`] = false
      window.jumpAppState[`isReady_The ${window.location.hash}`]}// Configure end first

    console.log('Configured')

    this.isPreConfigFinish = true
  },
  methods: {
    / / configuration first
    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()
      }
    },

    / / WeChat inside
    async initInWeixin() {
      console.log('Inside Wechat')

      / / get queryStr
      let queryStrArr = []
      for (let key in this.queryData) {
        const str = `${key}=The ${this.queryData[key]}` // name=tom
        queryStrArr.push(str)
      }
      const queryStr = queryStrArr.join('&')

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

      this.openWeappBtnHtml = `
          <wx-open-launch-weapp
              id="launch-btn"
              username="The ${this.ghid}"
              path="${jumpPath}" style="background-color:transparent;" > <template> <div style="width:800px; padding:100px; color:transparent; background-color:transparent; "> </div> </template> </ wX-open-launch-downloadp > '
      this.isInitWechatFinish = true
    },
    / / WeChat outside
    async initOutWeixin() {
      const self = this
      let res
      try {
        res = await window.tcb.callFunction({
          name: self.callFunctionName, // Provide the cloud function name of the UrlScheme service
          data: {
            path: self.path,
            queryData: self.queryData,
          }, 
        })
      } catch (e) {
        console.log('The cloud function failed', e)
      }
      console.log('Cloud function result', 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>

Copy the code

Attached is the code of WXJSSDK for H5 configuration in wechatconfigWx

/ * * *@param {Array<string>} jsApiArr
 * @returns* /
export function configWx(jsApiArr = [], openTagArr = []) {
  return new Promise(async (resolve, reject) => {
    if(! wx)return reject()

    wx.ready(function (res) {
      All interface calls must be made after the result of the config interface. Config is an asynchronous operation on the client side, so if the relevant interface needs to be called when the page loads, it must be called in the ready function to ensure correct execution. Interfaces that are invoked only when triggered by the user can be invoked directly without being placed in the ready function.
      console.log('WX ready I printed', res)
      return resolve()
    })

    wx.error(function (err) {
      If the config information fails to be verified, the error function will be executed. For example, the verification fails due to the expiration of the signature. You can view the specific error information in the debug mode of config or in the returned res parameter.
      console.log('Wx error I printed', err)
      return reject(err)
    })

    / / request
    let config = null
    try {
      /** * Apple share will fail to retrieve the signature because: Apple browser mechanism in wechat is different from Android, there are IOS cache problems, and IOS single page optimization problems. * Generally speaking, Android will refresh the current URL when jumping to share the page, but Apple will not, Apple through history records. The URL will not be refreshed so the signature will fail. * * so * get the full href of signUrl for Android, ios only uses */ before #
      let signUrl = ' '
      if (isIOS()) {
        signUrl = window.location.href.split(The '#') [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, // If debug mode is enabled, the return value of all API calls will be displayed in the client alert. If you want to view the parameters passed in, you can open it in the PC, and the parameter information will be printed in the log.
        debug: false.appId: appid + ' '.timestamp: config.timestamp + ' '.// Mandatory to generate the timestamp of the signature
        nonceStr: config.nonceStr + ' '.// Mandatory to generate a random string of signatures
        signature: config.signature + ' '.// Mandatory, signature
        jsApiList: ['hideMenuItems'. jsApiArr],// Mandatory, a list of JS interfaces that need to be used
        openTagList: [...openTagArr],
      })
    }
  })
}
Copy the code

Reference Documents:

Github web2app.js this is an official example, referring largely to urlscheme.generate