vue中如何使用wx内置组件 wx-open-launch-app / wx-open-launch-weapp

758 阅读9分钟

首先,请确保已配置JS安全域名并且完成了服务接入,参考文章 h5接微信js-sdk的详细步骤,前端步骤 1 2。

在vue2中,使用方式

第一种:

安装 wx 的 sdk

npm install weixin-js-sdk 或 yarn add weixin-js-sdk 或 pnpm install weixin-js-sdk

安装完成之后,第一种方式是直接在main.js文件中或对应的组件中引入,并且配置需要的功能

想要使用 wx-open-launch-weapp wx-open-launch-app 必须配置 openTagList

import wx from "weixin-js-sdk"; // 在main.js文件中引入

在组件中引入

<template>
  <div>
    <wx-open-launch-app :appid="appid" :extinfo="extinfo" @error="error" @launch="launch" @ready="ready">
      <script type="text/wxtag-template">
        <style>
          .btn{
            color:red;
          }
        </style>
        <button class="btn">按钮</button>
      </script>
    </wx-open-launch-app>
  </div>
</template>
<script>
import wx from "weixin-js-sdk";
export default {
  data(){
    return {
      appid:'',
      extinfo:JSON.stringify({'key':'value'})
    }
  },
  created(){
    init()
  },
  methods:{
    async init(){
      try{
        // 即使路由是 history 模式,即没有 # 号时,也可以返回正常的地址
        const url =location.href.split('#')[0]
        // 这里也可以对url进行加密后传递,请求后端获取签名的接口,返回小程序ID和签名等信息
        const {data}=await getSignature({
          url:url
        })
        // 获取到数据之后,将小程序ID等信息配置完整,我这里解构了一层直接获取到了数据,你也可以直接在getSignature方法中解构了返回
        wx.config({
          debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
          appId: data.appId, // 必填,公众号的唯一标识
          timestamp: data.timestamp, // 必填,生成签名的时间戳
          nonceStr: data.nonceStr, // 必填,生成签名的随机串
          signature: data.signature,// 必填,签名
          jsApiList: [
            'showOptionMenu',               // 显示右上角菜单接口
            'hideOptionMenu',               // 隐藏右上角菜单接口
            'onMenuShareAppMessage',        // 分享给朋友
            'onMenuShareTimeline',          // 分享到朋友圈
            'onMenuShareQQ',                // 分享到QQ
            'onMenuShareQZone',             // 分享到QQ空间
            'onMenuShareWeibo',             // 分享到微博
            'hideMenuItems',                // 批量隐藏功能按钮接口
            'showMenuItems',                // 批量显示功能按钮接口
            'hideAllNonBaseMenuItem',       // 隐藏所有非基础按钮接口
            'showAllNonBaseMenuItem'        // 显示所有功能按钮接口
          ],
          // 必填,需要使用的JS接口列表
          openTagList: ['wx-open-launch-weapp', 'wx-open-launch-app'] // 可选,需要使用的开放标签列表,例如['wx-open-launch-app']
        });
      }catch (e) {
        console.error('初始化微信SDK出现异常',e)
        return false
      }
      return new Promise((resolve, reject) => {
        wx.ready(() => {
          console.log('微信环境初始化微信SDK成功')
          resolve(true)
        })
        wx.error((e) => {
          console.error('初始化微信SDK失败', e)
          reject(false)
        })
      })
    },
    async getSignature(params){
      // 这里是项目获取签名的接口
      // return axios.post('xxx',params)
    },
    error(e){
      console.log('跳转失败',e)
    },
    launch(data){
      console.log('开始跳转',data)
    },
    ready(){
      console.log('准备好了')
    }
  }
}
</script>

第二种:封装后使用

新建 weChat.js 文件,实现预加载,分享功能封装等功能,在需要的组件中调用 preInit 方法即可。

想要使用 wx-open-launch-weapp wx-open-launch-app 必须配置 openTagList

// 判断是否是微信环境
function isWeChat(){
    return !!navigator.userAgent.match(/MicroMessenger/) && !navigator.userAgent.match(/wxwork/)
}
// 判断是不是企业微信环境
function isWxWork(){
    return !!navigator.userAgent.match(/MicroMessenger/) && !!navigator.userAgent.match(/wxwork/)
}
/**
 * 等待指定的时间
 * @param {number} time 等待的时间 毫秒
 * @returns {Promise<void>}
 */
async function wait(time) {
    return new Promise((resolve) => {
        setTimeout(resolve, time)
    })
}
/**
 * @typedef {'NO' | 'PENDING' | 'READY'} ReadyState NO: 未初始化, PENDING: 初始化中, READY: 初始化完成
 */

/**
 * 存储不同类型的初始化状态
 * @type {{symbo: ReadyState}}
 */
const readyStateMap = {}
// 等待初始化的最大时间
const MAX_WAIT_TIME = 3000
// 每次等待的时间
const PER_WAIT_TIME = 100

const wxReady=Symbol('WX')
/**
 * 等待初始化完成,如果已经是READY状态或NO状态则直接返回, 如果是PENDING状态则等待一段时间后再返回
 * @param {symbol} type
 * @returns {Promise<ReadyState>}
 */
export async function waitPending(type) {
    let state = getReadyState(type)
    if (state === `READY:${location.href.split('#')[0]}` || state === 'NO') return state

    // 如果正在初始化中则等待 并按照间隔时间检查是否初始化完成
    const times = MAX_WAIT_TIME / PER_WAIT_TIME
    for (let i = 0; i < times; i++) {
        await wait(PER_WAIT_TIME)
        state = getReadyState(type)
        if (state === `READY:${location.href.split('#')[0]}`) return state
        if (state === 'NO') break
    }

    return state
}

/**
 * 获取初始化状态
 * @param {symbol} type 要获取的类型
 */
export function getReadyState(type) {
    readyStateMap[type] = readyStateMap[type] || 'NO'
    return readyStateMap[type]
}

export function no(type) {
    readyStateMap[type] = 'NO'
}

export function pending(type) {
    readyStateMap[type] = 'PENDING'
}

export function ready(type) {
// 这里的状态记录,hash没有问题,但是history模式时 location.href.split('#')[0]得到的路径一直在变化,而导致返回的签名不同,在init方法中,判断状态时会被 return
    // readyStateMap[type] = 'READY'
    readyStateMap[type] = `READY:${location.href.split('#')[0]}`
}
// 创建 script 标签
function loadJS(js){
    return new Promise((resolve,reject)=>{
        const script=document.createElement('script')
        script.src=js
        script.onload=resolve
        script.onerror=reject
        document.head.appendChild(script)
    })
}
async function getSignature(params){
    // 这里是项目获取签名的接口
    // return axios.post('xxx',params)
}
/**
 * 分享类型
 * @typedef {Object} ShareType
 * @property {string} timeline 分享到朋友圈
 * @property {string} wechat 分享给微信好友
 * @property {string} chat 分享到对话 企微的转发 微信同分享给朋友
 * @property {string} qq 分享到QQ
 */
const shareType = {
    /**
     * 分享到朋友圈
     */
    timeline: 'timeline',
    /**
     * 分享到微信
     */
    wechat: 'wechat',
    /**
     * 分享到对话
     */
    chat: 'chat',
    /**
     * 分享到QQ
     */
    qq: 'qq'
}
/**
 * 初始化微信SDK
 * @param {boolean} [keepWait=true] 默认为true表示如果是pending则会尝试等待 否则不再等待直接返回错误
 */
async function init(keepWait=true){
    // 这里先同步获取状态
    const state = getReadyState(wxReady)
    // if (state === 'READY') return true
    if(state === `READY:${location.href.split('#')[0]}`) return true
    // 如果是pending 则根据 keepWait判断是继续等待 还是 直接返回
    if (state === 'PENDING') {
        if (keepWait) {
            await waitPending(wxReady)
            return init(false)
        } else {
            return false
        }
    }
    // 判断是不是微信环境,是微信环境则初始化微信SDK
    if(isWeChat()){
        console.log("当前环境不是微信环境,不在继续初始化微信SDK")
        return false
    }
    console.log("继续初始化微信SDK")
    pending(wxReady)
    try{
        // 在需要调用 JS 接口的页面引入如下 JS 文件,(支持https):https://res.wx.qq.com/open/js/jweixin-1.6.0.js
        // 如需进一步提升服务稳定性,当上述资源不可访问时,可访问(支持https):https://res2.wx.qq.com/open/js/jweixin-1.6.0.js。
        if(!window.wx){
            await loadJS('https://res.wx.qq.com/open/js/jweixin-1.6.0.js')
        }
    }catch (e) {
        console.error("加载微信JS失败",e)
        return false
    }
    const wx=window.wx
    console.log('微信环境初始化微信SDK成功')

    try{
        // 即使路由是 history 模式,即没有 # 号时,也可以返回正常的地址
        const url =location.href.split('#')[0]
        // 这里也可以对url进行加密后传递,请求后端获取签名的接口,返回小程序ID和签名等信息
        const {data}=await getSignature({
            url:url
        })
        // 获取到数据之后,将小程序ID等信息配置完整,我这里解构了一层直接获取到了数据,你也可以直接在getSignature方法中解构了返回
        wx.config({
            debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
            appId: data.appId, // 必填,公众号的唯一标识
            timestamp: data.timestamp, // 必填,生成签名的时间戳
            nonceStr: data.nonceStr, // 必填,生成签名的随机串
            signature: data.signature,// 必填,签名
            jsApiList: [
                'showOptionMenu',               // 显示右上角菜单接口
                'hideOptionMenu',               // 隐藏右上角菜单接口
                'onMenuShareAppMessage',        // 分享给朋友
                'onMenuShareTimeline',          // 分享到朋友圈
                'onMenuShareQQ',                // 分享到QQ
                'onMenuShareQZone',             // 分享到QQ空间
                'onMenuShareWeibo',             // 分享到微博
                'hideMenuItems',                // 批量隐藏功能按钮接口
                'showMenuItems',                // 批量显示功能按钮接口
                'hideAllNonBaseMenuItem',       // 隐藏所有非基础按钮接口
                'showAllNonBaseMenuItem'        // 显示所有功能按钮接口
            ],
            // 必填,需要使用的JS接口列表
            // 想要使用 wx-open-launch-weapp  wx-open-launch-app 必须配置这项
            openTagList: ['wx-open-launch-weapp', 'wx-open-launch-app'] // 可选,需要使用的开放标签列表,例如['wx-open-launch-app']
        });
    }catch (e) {
        no(wxReady)
        console.error('初始化微信SDK出现异常',e)
        return false
    }
    return new Promise((resolve, reject) => {
        wx.ready(() => {
            console.log('微信环境初始化微信SDK成功')
            ready(wxReady)
            resolve(true)
        })

        wx.error((e) => {
            no(wxReady)
            console.error('初始化微信SDK失败', e)
            reject(false)
        })
    })


}

const supportShareType = {
    [shareType.timeline]: 'onMenuShareTimeline',
    [shareType.wechat]: 'onMenuShareAppMessage',
    [shareType.chat]: 'onMenuShareAppMessage',
    [shareType.qq]: 'onMenuShareQQ'
}

/**
 * 分享选项
 * @typedef {Object} ShareOptions
 * @property {string} title 分享标题
 * @property {string} desc 分享描述
 * @property {string} link 分享链接 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
 * @property {string} imgUrl 分享图标
 * @property {'music' | 'video' | 'link'} type 分享类型,music、video或link,不填默认为link; 分享给朋友时才有该参数
 * @property {string} dataUrl 如果type是music或video,则要提供数据链接,默认为空;
 * @property {Array<keyof ShareType>} shareType 分享类型
 */

const wxClient = {
    /**
     * 预加载
     */
    preInit: init,

    /**
     * 分享类型
     * @type {ShareType}
     */
    shareType,

    /**
     * 设置分享 支持同时设置多个不同的分享 如同时设置分享到朋友圈和朋友
     * @param options {ShareOptions} 分享选项
     * @returns {Promise<boolean>}
     */
    async share(options) {
        if (!(await init())) {
            return false
        }
        return new Promise((resolve, reject) => {
            const shareMethods = new Set()
            for (const shareType of options.shareType) {
                const shareMethod = supportShareType[shareType]
                if (shareMethod) {
                    shareMethods.add(shareMethod)
                }
            }

            const shareMethodsLength = shareMethods.size
            let successCount = 0
            const success = () => {
                console.log('分享成功')
                successCount++
                if (successCount === shareMethodsLength) {
                    resolve(true)
                }
            }

            const error = (e) => {
                console.error('分享失败', e)
                reject(false)
            }

            for (const shareMethod of shareMethods) {
                window.wx[shareMethod]({ ...options, success, cancel: error, error })
            }
        })
    }
}

const wxProxy = new Proxy(wxClient, {
    get(target, key) {
        // 如果原对象中存在该属性 则直接返回
        if (Reflect.has(target, key)) {
            return Reflect.get(target, key)
        }

        return async function(...args) {
            await init()
            const wx = window.wx
            console.log('当前请求的key', key)
            if (typeof wx[key] === 'function') {
                return wx[key].apply(wx, args)
            } else {
                return wx[key]
            }
        }
    }
})

export default wxProxy

在组件中调用 preInit 方法,直接在 template 中写 wx-open-launch-app 标签即可。

<template>
  <div>
    <wx-open-launch-app :appid="appid" :extinfo="extinfo" @error="error" @launch="launch" @ready="ready">
      <script type="text/wxtag-template">
        <style>
          .btn{
            color:red;
          }
        </style>
        <button class="btn">按钮</button>
      </script>
    </wx-open-launch-app>
  </div>
</template>
<script>
import Wx from './weChat'
  export default {
    data(){
      return {
        appid:'',
        extinfo:JSON.stringify({'key':'value'})
      }
    },
    created(){
      Wx.preInit()
    },
    methods:{
      error(e){
        console.log('跳转失败',e)
      },
      launch(data){
        console.log('开始跳转',data)
      },
      ready(){
        console.log('准备好了')
      }
    }
  }
</script>

在vue3中,使用方式

在vue3中使用时,js逻辑不变,需要注意的是在 template 中,使用方式有所区别。 <script type="text/wxtag-template"></script> 需要使用 component 组件渲染。

<template>
  <div>
    <wx-open-launch-app :appid="appid" :extinfo="extinfo" @error="error" @launch="launch" @ready="ready">
      <component :is="'script'" type="text/wxtag-template">
        <div style="color:red;">
          按钮
        </div>
      </component>
    </wx-open-launch-app>
  </div>
</template>
<script setup>
import wx from "weixin-js-sdk";
import {ref} from 'vue'
// 如果这两个值不会改变可以不用ref
const appid=ref('')
const extinfo=ref(JSON.stringify({'key':'value'}))
const init=async()=>{
  try{
    // 即使路由是 history 模式,即没有 # 号时,也可以返回正常的地址
    const url =location.href.split('#')[0]
    // 这里也可以对url进行加密后传递,请求后端获取签名的接口,返回小程序ID和签名等信息
    const {data}=await getSignature({
      url:url
    })
    // 获取到数据之后,将小程序ID等信息配置完整,我这里解构了一层直接获取到了数据,你也可以直接在getSignature方法中解构了返回
    wx.config({
      debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
      appId: data.appId, // 必填,公众号的唯一标识
      timestamp: data.timestamp, // 必填,生成签名的时间戳
      nonceStr: data.nonceStr, // 必填,生成签名的随机串
      signature: data.signature,// 必填,签名
      jsApiList: [
        'showOptionMenu',               // 显示右上角菜单接口
        'hideOptionMenu',               // 隐藏右上角菜单接口
        'onMenuShareAppMessage',        // 分享给朋友
        'onMenuShareTimeline',          // 分享到朋友圈
        'onMenuShareQQ',                // 分享到QQ
        'onMenuShareQZone',             // 分享到QQ空间
        'onMenuShareWeibo',             // 分享到微博
        'hideMenuItems',                // 批量隐藏功能按钮接口
        'showMenuItems',                // 批量显示功能按钮接口
        'hideAllNonBaseMenuItem',       // 隐藏所有非基础按钮接口
        'showAllNonBaseMenuItem'        // 显示所有功能按钮接口
      ],
      // 必填,需要使用的JS接口列表
      openTagList: ['wx-open-launch-weapp', 'wx-open-launch-app'] // 可选,需要使用的开放标签列表,例如['wx-open-launch-app']
    });
  }catch (e) {
    console.error('初始化微信SDK出现异常',e)
    return false
  }
  return new Promise((resolve, reject) => {
    wx.ready(() => {
      console.log('微信环境初始化微信SDK成功')
      resolve(true)
    })
    wx.error((e) => {
      console.error('初始化微信SDK失败', e)
      reject(false)
    })
  })
}
init()
const getSignature=async (params)=>{
  // 这里是项目获取签名的接口
  // return axios.post('xxx',params)
}
const  error=(e)=>{
  console.log('跳转失败',e)
}
const launch=(data)=>{
  console.log('开始跳转',data)
}
const ready=()=>{
  console.log('准备好了')
}
</script>

此时,控制台会报错,不能识别标签 wx-open-launch-app,如果是vite项目,需要在vite.config.js文件中配置以下内容。

isCustomElement 是指自定义的标签是什么,解析时会自动忽略当前标签

如果不喜欢在组件中直接引入wx的 sdk,也可以使用上面封装后的方法。

参考文章 vue3中使用 wx-open-launch-app 标签及解决控制台警告的问题

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
  return {
    plugins: [
      vue(
          {
            template: {
              compilerOptions: {
                 // 忽略wx-open-launch 开头的组件,这些是微信的默认组件
                isCustomElement: (tag) => tag.includes('wx-open-launch')
              }
            }
          }
      ),
    ],
  }
})

参考文章

h5接微信js-sdk的详细步骤

vue3中使用 wx-open-launch-app 标签及解决控制台警告的问题