## 开放标签使用说明

## 概述

微信开放标签是微信公众平台面向网页开发者提供的扩展标签集合。通过使用微信开放标签，网页开发者可安全便捷地使用微信或系统的能力，为微信用户提供更优质的网页体验。

此文档面向网页开发者，介绍微信开放标签如何使用及相关注意事项。需要注意的是，微信开放标签有最低的微信版本要求、最低的系统版本要求，以及最低的JS接口文件版本要求。

- 微信版本要求为：7.0.12及以上（如果是鸿蒙系统，则微信版本要求为：1.0.3 及以上）
- 系统版本要求为：iOS 10.3及以上、Android 5.0及以上
- JS接口文件：1.6.0以上 （例如：http://res.wx.qq.com/open/js/jweixin-1.6.0.js）
- 使用开放标签的网页不可以作为iframe嵌入其他网页

对于符合微信或系统最低版本要求但仍无法使用微信开放标签的场景，将会在下方使用步骤中的`wx.config`权限验证成功后触发`WeixinOpenTagsError`事件告知开发者。仅无法使用微信开发标签，JS-SDK其他功能不受影响。可通过如下方法监听并进行回退兼容：

```js
document.addEventListener('WeixinOpenTagsError', function (e) {
  console.error(e.detail.errMsg); // 无法使用开放标签的错误原因，需回退兼容。仅无法使用开放标签，JS-SDK其他功能不受影响
});
```

根据目前已知的错误场景，回退兼容建议如下：

1. iOS15底层WebKit接口发生变更，微信版本8.0.8以下（不包括8.0.8）无法使用开放标签，可引导用户升级最新版本微信；
2. 开放标签依赖Web Components方案，极少部分Android系统可能由于版本太低而不支持，可引导用户升级系统固件。

**⚠️  特别注意（SPA / 前端路由场景）**：开放标签的能力权限是与当前页面url绑定的。对于SPA（单页应用），当通过前端路由更新url后，**必须及时使用新的url重新调用`wx.config`**，否则会因为url的变更导致已申请的开放标签能力权限被回收，从而无法使用开放标签。

## 使用步骤

微信开放标签使用步骤与微信JS-SDK类似，也需要引入JS文件等步骤。如果是服务号身份的网页，需要绑定安全域名，如果是使用小程序云开发[静态网站托管](/miniprogram/dev/wxcloudservice/wxcloud/guide/staticstorage/introduction.md)的小程序网页，则不需绑定安全域名即可直接使用（即跳过下面"步骤一：绑定安全域名"）。

#### 1. 绑定域名

登录[微信公众平台](https://mp.weixin.qq.com)进入“服务号设置”的“功能设置”里填写“JS接口安全域名”。

#### 2. 引入JS文件

在需要调用JS接口的页面引入如下JS文件：http://res.wx.qq.com/open/js/jweixin-1.6.0.js （支持https）

如需进一步提升服务稳定性，当上述资源不可访问时，可改访问：http://res2.wx.qq.com/open/js/jweixin-1.6.0.js （支持https）

备注：支持使用 AMD/CMD 标准模块加载方法加载。

#### 3. 通过config接口注入权限验证配置并申请所需开放标签

与使用JS-SDK配置方式相同，所有需要使用开放标签的页面必须先注入配置信息，并通过`openTagList`字段申请所需要的开放标签，否则将无法使用（同一个url仅需调用一次）。开放标签的申请和JS接口的申请相互独立，因此是可以同时申请的。

```js
wx.config({
  debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来，若要查看传入的参数，可以在pc端打开，参数信息会通过log打出，仅在pc端时才会打印
  appId: '', // 必填，服务号的唯一标识
  timestamp: , // 必填，生成签名的时间戳
  nonceStr: '', // 必填，生成签名的随机串
  signature: '',// 必填，签名
  jsApiList: [], // 必填，需要使用的JS接口列表
  openTagList: [] // 可选，需要使用的开放标签列表，例如['wx-open-launch-app']
});
```

签名算法见JS-SDK说明文档的[附录](jssdk)，所有开放标签列表见文末的附录1。

#### 4. 通过ready接口处理成功验证

```js
wx.ready(function () {
  // config信息验证后会执行ready方法，所有接口调用都必须在config接口获得结果之后，config是一个客户端的异步操作，所以如果需要在页面加载时就调用相关接口，则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口，则可以直接调用，不需要放在ready函数中
});
```

#### 5. 通过error接口处理失败验证

```js
wx.error(function (res) {
  // config信息验证失败会执行error函数，如签名过期导致验证失败，具体错误信息可以打开config的debug模式查看，也可以在返回的res参数中查看，对于SPA可以在这里更新签名
});
```

## 使用说明

所有开放标签都能像普通的HTML标签一样在页面中直接使用，不需要再进行额外的处理。

如果所使用的标签允许提供插槽，由于插槽中模版的样式是和页面隔离的，因此需要注意在插槽中定义模版的样式。插槽模版及样式均需要通过`<script type="text/wxtag-template"></script>`进行包裹。另外，对于具名插槽还需要通过`slot`属性声明插槽名称，下文标签插槽中的default插槽为默认插槽，可不声明插槽名称。

对于标签事件，均可通过`event.detail`获得详细信息。如果无特殊说明，下文标签事件说明中的返回值均指代`event.detail`中的内容。

**另外，需要注意以下几点：**

1. 页面中与布局和定位相关的样式，如`position: fixed; top -100;`等，尽量不要写在插槽模版的节点中，请声明在标签或其父节点上；
2. 对于有CSP要求的页面，需要添加白名单`frame-src https://*.qq.com webcompt:`，才能在页面中正常使用开放标签。

## 开放标签

### 跳转小程序：wx-open-launch-weapp

用于页面中提供一个可跳转指定小程序的按钮。使用此标签后，用户需在网页内点击标签按钮方可跳转小程序。H5通过开放标签打开小程序的场景值为1167。

##### 开放对象

1. **已认证的服务号**，服务号绑定“JS接口安全域名”下的网页可使用此标签跳转任意合法合规的小程序。
2. 已认证的非个人主体的小程序，使用小程序云开发的[静态网站托管](/miniprogram/dev/wxcloudservice/wxcloud/guide/staticstorage/introduction.md)绑定的域名下的网页，可以使用此标签跳转任意合法合规的小程序。

##### 错误提示

若跳转时出现以下页面，表示网页绑定的服务号或小程序无权限，请检查是否符合上述开放对象条件。

![无权限提示](https://res.wx.qq.com/op_res/AVNO5G9YvNvN45ZU0budE1Xv6i1NfyHD-DYJnDyLxv6yjFVhwD_SqdzWxEKAtaPs)

##### 属性

| 名称     | 必填 | 默认值 | 备注                 |
| -------- | ---- | ------ | -------------------- |
| appid    | 是   |        | 所需跳转的小程序appid，即小程序对应的以`wx`开头的id |
| username | 否   |        | 所需跳转的小程序原始id，即小程序对应的以`gh_`开头的id（跳转时，有appid会优先使用appid，没有appid才会使用username） |
| path     | 否   |        | 所需跳转的小程序内页面路径及参数 |
| env-version | 否 | release | 所需跳转的小程序版本，合法值为：正式版release、开发版develop、体验版trial（支持的微信版本：iOS 8.0.18及以上、Android 8.0.19及以上） |
| extra-data  | 否 |         | 以JSON格式字符串向所需跳转的小程序传递数据。小程序可在`App.onLaunch`、`App.onShow`等中获取；小游戏可在`wx.onAppShow`、`wx.getLaunchOptionsSync`等中获取（支持的微信版本：iOS 8.0.18及以上、Android 8.0.19及以上） |

##### 插槽

| 名称    | 必填 | 默认值 | 备注      |
| ------- | ---- | ------ | --------- |
| default | 是   |        | 跳转按钮模版及样式 |

##### 事件

| 名称   | 冒泡   | 返回值             | 备注                  |
| ------ | ------ | ------------------ | --------------------- |
| ready  | 否     |                    | 标签初始化完毕，可以进行点击操作 |
| launch | 否     | { appId: string, userName: string, path: string } | 用户点击跳转按钮并对确认弹窗进行操作后触发 |
| error  | 否     | { errMsg: string, appId: string, userName: string, path: string } | 用户点击跳转按钮后出现错误 |

备注：`error`事件返回值`errMsg`说明如下。

| errMsg        | 说明     |
| ------------- | -------- |
| "launch:fail" | 跳转失败 |

##### 用例

```html
<wx-open-launch-weapp
  id="launch-btn"
  appid="wx12345678"
  path="pages/home/index?user=123&action=abc"
>
  <script type="text/wxtag-template">
    <style>.btn { padding: 12px }</style>
    <button class="btn">打开小程序</button>
  </script>
</wx-open-launch-weapp>
<script>
  var btn = document.getElementById('launch-btn');
  btn.addEventListener('launch', function (e) {
    console.log('success');
  });
  btn.addEventListener('error', function (e) {
    console.log('fail', e.detail);
  });
</script>
```

### 跳转APP：wx-open-launch-app

用于页面中提供一个可跳转指定App的按钮。**注意：Android/鸿蒙平台通过开放标签跳转App，App必须接入微信OpenSDK**，详细参见文档[《Android微信OpenSDK接入指南》](/doc/oplatform/Mobile_App/Access_Guide/Android.md) [鸿蒙微信OpenSDK接入指南](/doc/oplatform/Mobile_App/Access_Guide/ohos.md)。

补充说明：1、必须真机才能渲染该标签；2、文字链无法拉起该标签

##### 开放对象

此功能仅开放给**已认证的服务号**，服务号绑定“JS接口安全域名”下的网页可使用此标签跳转满足一定条件的App。在使用该标签之前，首先需要前往[微信开放平台](https://open.weixin.qq.com)的**管理中心-公众账号或小程序详情-接口信息-网页跳转移动应用-关联设置**中绑定所需要跳转的App。详细配置规则参考文档[《微信内网页跳转APP功能》](/doc/oplatform/Mobile_App/WeChat_H5_Launch_APP.md)。

##### 属性

| 名称    | 必填  | 默认值 | 备注                       |
| ------- | ----- | ------ | -------------------------- |
| appid   | 是    |        | 所需跳转的移动应用的AppID  |
| extinfo | 否    |        | 跳转所需额外信息           |

备注：对于`extinfo`属性，用于携带额外信息，格式自定义，由跳转的App自⾏解析处理。对应iOS微信OpenSDK中的messageExt字段（LaunchFromWXReq.message.messageExt），或对应Android微信OpenSDK中的messageExt字段（ShowMessageFromWX.Req.message.messageExt），详细参见文档[《App获取开放标签\中的extinfo数据》](opentag)。

##### 插槽

| 名称    | 必填 | 默认值 | 备注      |
| ------- | ---- | ------ | --------- |
| default | 是   |        | 跳转按钮模版及样式 |

##### 事件

| 名称   | 冒泡   | 返回值             | 备注                  |
| ------ | ------ | ------------------ | --------------------- |
| ready  | 否     |                    | 标签初始化完毕，可以进行点击操作           |
| launch | 否     | { appId: string, extInfo: string } | 用户点击跳转按钮并对确认弹窗进行操作后触发 |
| error  | 否     | { errMsg: string, appId: string, extInfo: string } | 用户点击跳转按钮后出现错误                 |

备注：`error`事件返回值`errMsg`说明如下。

| errMsg                   | 说明                                     |
| ------------------------ | ---------------------------------------- |
| "launch:fail"            | 当前场景不支持跳转，或Android上该应用未安装，或iOS上用户在弹窗上点击确认但该应⽤未安装 |
| "launch:fail_check fail" | 校验App跳转权限失败，请确认是否正确绑定AppID以及域名是否完全一致（详见[《微信内网页跳转APP功能》](/doc/oplatform/Mobile_App/WeChat_H5_Launch_APP.md)） |

##### 用例

```html
<wx-open-launch-app
  id="launch-btn"
  appid="your-appid"
  extinfo="your-extinfo"
>
  <script type="text/wxtag-template">
    <style>.btn { padding: 12px }</style>
    <button class="btn">App内查看</button>
  </script>
</wx-open-launch-app>
<script>
  var btn = document.getElementById('launch-btn');
  btn.addEventListener('launch', function (e) {
    console.log('success');
  });
  btn.addEventListener('error', function (e) {
    console.log('fail', e.detail);
  });
</script>
```

### 服务号订阅通知按钮：wx-open-subscribe

服务号提供在网页端设置订阅通知的能力。调起客户端服务号订阅通知界面，返回用户订阅通知的操作结果。

开放对象：已认证的服务号。服务号绑定“JS接口安全域名”下的网页可使用此标签调起订阅。

具体表现如图：

##### 注意事项

- 开放标签有最低的微信版本和系统版本要求，无法渲染出订阅按钮时，开发者需自行做低版本处理。
- 一次性模板id和永久模板id不可同时使用。
- 开发者工具里支持清除授权和模拟订阅，目前为假数据模拟，实际效果请在真机调试。

##### 属性

| 名称     | 必填 | 默认值 | 备注                         |
| -------- | ---- | ------ | ---------------------------- |
| template | 是   |        | 模版id，多个模版id以逗号隔开 |

##### 插槽

| 名称    | 必填 | 默认值 | 备注               |
| ------- | ---- | ------ | ------------------ |
| default | 是   |        | 订阅按钮模版及样式 |
| style   | 否   | 空 | 用于集中定义default插槽所需用到的样式 |

##### 事件

| 名称    | 冒泡 | 返回值                                             | 备注                 |
| ------- | ---- | -------------------------------------------------- | -------------------- |
| success | 否   | {errMsg: 'subscribe:ok', subscribeDetails: string} | 订阅按钮操作成功事件 |
| error   | 否   | {errMsg: string, errCode: string}                 | 订阅按钮操作失败事件 |

success事件属性说明

| 属性             | 类型   | 说明                                                         |
| ---------------- | ------ | ------------------------------------------------------------ |
| errMsg           | string | 按钮操作成功时errMsg值为'subscribe:ok'                       |
| subscribeDetails | string | [TEMPLATE_ID]是动态的键，即模版id，值包括：'accept'、'reject'、'cancel'、'filter'，'accept'表示用户同意订阅该条id对应的模版消息，'reject'表示用户拒绝订阅该条id对应的模版消息，'cancel'表示用户取消订阅该条id对应的模版消息，'filter'表示该模版应该标题同名被后台过滤。例如：{ errMsg: "subscribe:ok", subscribeDetails: "{"TenvU22BA1jCp4YHfYEpRuESXYReQyDuhs4vbdWA99I":"{\\"status\\":\\"accept\\"}"}"表示用户同意订阅TenvU22BA1jCp4YHfYEpRuESXYReQyDuhs4vbdWA99I这条消息。|

error事件属性说明

| 属性    | 类型   | 说明                     |
| ------- | ------ | ------------------------ |
| errMsg  | string | 订阅按钮调用失败错误信息 |
| errCode | string | 订阅按钮调用失败错误码   |

备注：`error`事件返回值`errMsg`和`errCode`说明如下。

| errCode | errMsg                                                 | 说明                                                       |
| ------- | ------------------------------------------------------ | ---------------------------------------------------------- |
| 10001   | template can't be empty                                | 参数传空了                                                 |
| 10002   | Request list fail                                      | 网络问题，请求消息列表失败                                   |
| 10003   | Request subscribe fail                                 | 网络问题，订阅请求发送失败                                   |
| 10004   | Invalid template id                                    | 参数类型错误                                               |
| 20001   | No template data return, verify the template id exist  | 没有模板数据，一般是模板 ID 不存在 或者和模板类型不对应 导致的    |
| 20002   | Templates type must be same                            | 模板消息类型 既有一次性的又有永久的                          |
| 20003   | Templates count out of max bounds                      | 模板消息数量超过上限                                         |
| 20004   | The main switch is switched off                        | 用户关闭了主开关，无法进行订阅                               |
| 20005   | This mini program was banned from subscribing messages | 服务号被封禁                                                 |

##### 用例

```html
<wx-open-subscribe template="TenvU22BA1jCp4YHfYEpRuESXYReQyDuhs4vbdWA99I" id="subscribe-btn">
  <script type="text/wxtag-template" slot="style">
    <style>
      .subscribe-btn {
        color: #fff;
        background-color: #07c160;
      }
    </style>
  </script>
  <script type="text/wxtag-template">
    <button class="subscribe-btn">
      一次性模版消息订阅              
    </button>
  </script>
</wx-open-subscribe>
<script>
var btn = document.getElementById('subscribe-btn');
btn.addEventListener('success', function (e) {            
  console.log('success', e.detail);
});   
btn.addEventListener('error',function (e) {             
  console.log('fail', e.detail);
});
</script>
```

### 音频播放：wx-open-audio

扩展音频标签，用于接入微信浮窗播放器。

##### 开放对象

此功能仅开放给**已认证的服务号**，服务号绑定“JS接口安全域名”下的网页可使用此标签进行浮窗音频播放。

##### 属性

| 名称    | 必填  | 默认值 | 备注             |
| ------- | ----- | ------ | ---------------- |
| src     | 是    |    -   | 设置音频播放链接  |
| title   | 否    | "unknown" | 设置音频名称 |
| episode | 否    | "unknown" | 设置专辑名称 |
| singer  | 否    | "unknown" | 设置歌手名称 |
| cover   | 否    | "" | 设置专辑封面 |

```html
<wx-open-audio
  title="别找我麻烦"
  singer="蔡健雅"
  episode="说到爱"
  src="http://URL地址/.mp3"
  cover="http://URL地址/xxx.jpg"
></wx-open-audio>
```

##### 插槽

该标签的视图可以理解成是一个播放按钮，因此具有以下插槽：

| 名称    | 必填 |默认值 | 备注      |
| ------- | ---- | ---- |--------- |
| default | 否   | - | 播放按钮默认视图，用于音频未在播放状态时显示，如果没有，默认显示微信音频播放按钮(40px*40px) |
| playing | 否   | - | 用于音频在播放状态时显示，如果没有，默认显示default插槽视图或者微信音频播放按钮 |
| style   | 否   | 空 | 用于集中定义default插槽和playing插槽所需用到的样式 |

```html
<wx-open-audio src="http://URL地址/.mp3">
  <script type="text/wxtag-template">
    <div class="playBackground"></div>
  </script>
  <script type="text/wxtag-template" slot="playing">
    <div class="pauseBackground"></div>
  </script>
  <script type="text/wxtag-template" slot="style">
    <style>
      .playBackground {
        width: 60px;
        height: 64px;
        background: url("xxx");
        background-repeat: no-repeat;
        background-size: 100%;
        cursor: pointer;
      }
      .pauseBackground {
        width: 60px;
        height: 64px;
        background: url("xxx");
        background-repeat: no-repeat;
        background-size: 100%;
        cursor: pointer;
      }
    </style>
  </script>
</wx-open-audio>
```

##### 实例属性

| 名称     | 备注             |
| ------- | ---------------- |
| src     | 获取和设置音频播放链接 |
| title   |  获取和设置音频名称 |
| episode | 获取和设置专辑名称 |
| singer  | 获取和设置歌手名称 |
| cover   | 获取和设置专辑封面 |
| ended   | 获取音频目前是否结束播放 |
| paused  | 获取音频目前是否暂停播放 |
| duration | 获取音频长度，单位秒 |
| readyState | 获取音频当前就绪状态，含义对齐[W3C标准](https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLMediaElement/readyState)，但只有1/2/4三种状态  |
| currentTime | 获取和设置播放进度，单位秒（\*音频正在后台播放时或正在后台暂停时设置才生效） |
| playbackRate | 微信7.0.15及以上支持获取和设置播放速率，范围0~2 (\*音频正在后台播放时设置才生效) |
| error  | 获取音频相关错误信息 |

备注：`error`属性说明如下。

| 错误           | 说明     |
| ------------- | -------- |
| "play:fail"   | 调用play方法失败 |
| "pause:fail"  | 调用pause方法失败 |
| "seek:fail"   | 设置currentTime失败 |
| "ratechange:fail" | 设置playbackRate失败 |

```js
// <wx-open-audio id="audio"></wx-open-audio>
const audio = document.getElementById('audio');
console.log(audio.src);
console.log(audio.title);
console.log(audio.episode);
console.log(audio.singer);
console.log(audio.cover);
console.log(audio.error);
console.log(audio.ended);
console.log(audio.paused);
console.log(audio.duration);
console.log(audio.readyState);
console.log(audio.currentTime);
console.log(audio.playbackRate);
audio.src = 'http://URL地址/.mp3'
audio.title = '别找我麻烦'
audio.episode = '说到爱'
audio.singer = '蔡健雅'
audio.cover = 'http://URL地址/xxx.jpg'
audio.currentTime = 100;
audio.playbackRate = 1.0；
```

##### 实例方法

| 名称     | 备注             |
| ------- | ---------------- |
| load   | 重新加载音频 |
| play   | 播放音频，出于用户体验考虑，只有在用户首次点击触发播放后，才允许调用 |
| pause   | 暂停播放音频 |

```js
// <wx-open-audio id="audio"></wx-open-audio>
const audio = document.getElementById('audio');
audio.load();
audio.play();
audio.pause();
```

##### 事件

| 名称   | 冒泡   | 可取消             | 备注                  |
| ------ | ------ | ------------------ | --------------------- |
| ready  | 否     |        否            | 音频标签初始化完成 |

```js
// <wx-open-audio id="audio"></wx-open-audio>
const audio = document.getElementById('audio');
audio.addEventListener('ready', function (event) {
  // 标签初始化完成
});
```

| 名称   | 冒泡   | 可取消             | 备注                  |
| ------ | ------ | ------------------ | --------------------- |
| durationchange/loadedmetadata  | 否 | 否 | 拉取音频meta信息成功 |

```js
// <wx-open-audio id="audio"></wx-open-audio>
const audio = document.getElementById('audio');
audio.addEventListener('durationchange', function (event) {
  // 获取到音频长度
  const state = event.detail;
  console.log(state.duration);
});
audio.addEventListener('loadedmetadata', function (event) {
  // 音频meta信息只有长度，因此loadedmetadata会接着马上触发
  const state = event.detail;
  console.log(state.duration);
});
```

| 名称   | 冒泡   | 可取消             | 备注                  |
| ------ | ------ | ------------------ | --------------------- |
| canplay  | 否     |        否            | 音频可以播放 |

```js
// <wx-open-audio id="audio"></wx-open-audio>
const audio = document.getElementById('audio');
audio.addEventListener('canplay', function (event) {
  // 可随时播放
  const state = event.detail;
  console.log(state.duration);
});
```

| 名称   | 冒泡   | 可取消             | 备注                  |
| ------ | ------ | ------------------ | --------------------- |
| play  | 否     |        否            | 音频开始播放 |

```js
// <wx-open-audio id="audio"></wx-open-audio>
const audio = document.getElementById('audio');
audio.addEventListener('play', function (event) {
  // 开始播放
});
```

| 名称   | 冒泡   | 可取消             | 备注                  |
| ------ | ------ | ------------------ | --------------------- |
| pause  | 否     |        否            | 音频停止播放 |

```js
// <wx-open-audio id="audio"></wx-open-audio>
const audio = document.getElementById('audio');
audio.addEventListener('pause', function (event) {
  // 停止播放
});
```
| 名称   | 冒泡   | 可取消             | 备注                  |
| ------ | ------ | ------------------ | --------------------- |
| timeupdate  | 否     |        否        | 播放时，音频相关信息同步 |

```js
// <wx-open-audio id="audio"></wx-open-audio>
const audio = document.getElementById('audio');
audio.addEventListener('timeupdate', function (event) {
  const state = event.detail;
  console.log(state.src);         // 当前音频来源
  console.log(state.title);       // 当前音频标题
  console.log(state.episode);     // 当前专辑名称
  console.log(state.singer);      // 当前歌手名称
  console.log(state.cover);       // 当前专辑封面
  console.log(state.playState);   // 播放状态，'playing' | 'pause' | 'seeked'
  console.log(state.paused);      // 是否暂停
  console.log(state.buffered);    // 已缓冲位置，单位秒
  console.log(state.currentTime); // 当前播放位置，单位秒
});
```
| 名称   | 冒泡   | 可取消             | 备注                  |
| ------ | ------ | ------------------ | --------------------- |
|seeked  | 否     |        否            |设置currentTime以后，音频播放位置变更完成|

```js
// <wx-open-audio id="audio"></wx-open-audio>
const audio = document.getElementById('audio');
audio.addEventListener('seeked', function (event) {
  // 播放位置变更完成
  const state = event.detail;
  console.log(state.currentTime); // 变更后播放位置，单位秒
});
```
| 名称   | 冒泡   | 可取消             | 备注                  |
| ------ | ------ | ------------------ | --------------------- |
|ratechange | 否     |        否            |设置playbackRate以后，音频播放速率变更完成（微信7.0.15及以上支持）|

```js
// <wx-open-audio id="audio"></wx-open-audio>
const audio = document.getElementById('audio');
audio.addEventListener('ratechange', function (event) {
  // 播放位置变更完成
  const state = event.detail;
  console.log(state.playbackRate); // 变更后播放速率
});
```

| 名称   | 冒泡   | 可取消             | 备注                  |
| ------ | ------ | ------------------ | --------------------- |
| ended | 否     |        否            |音频播放结束|

```js
// <wx-open-audio id="audio"></wx-open-audio>
const audio = document.getElementById('audio');
audio.addEventListener('ended', function (event) {
  // 播放结束
});
```
| 名称   | 冒泡   | 可取消             | 备注                  |
| ------ | ------ | ------------------ | --------------------- |
| error | 否     |        否            |音频加载、播放或操作过程中发生错误|

```js
// <wx-open-audio id="audio"></wx-open-audio>
const audio = document.getElementById('audio');
audio.addEventListener('error', function (event) {
  // 发生错误
  const error = event.detail;
  console.log(error.errMsg);
  // 1. play:fail（调用play方法失败）
  // 2. pause:fail（调用pause方法失败）
  // 3. seek:fail（设置currentTime失败）
  // 4. ratechange:fail（设置playbackRate失败）
});
```

## 在Vue、React等框架中使用

插槽模版的内容和样式与主页面相互隔离，使用数据驱动的方式去更改插槽模版中的内容和样式将不会得到更新。

### 在Vue中使用

开放标签属于自定义标签，Vue会给予未知标签的警告，可通过配置`Vue.config.ignoredElements`来忽略Vue对开放标签的检查。

```html
<wx-open-audio
  :title="title"
  src="http://URL地址/.mp3"
  @canplay="canplay"
  @pause="pause"
  @seeked="seeked"
>
  <script type="text/wxtag-template">
    <button>播放</button>
  </script>
</wx-open-audio>
```

### 在React中使用

因为不兼容合成事件，所以需要绑定原生事件。

```js
function Audio() {
  const audio = useRef(null);
  useEffect(() => {
    const canplay = () => {};
    const pause = () => {};
    const seeked = () => {};
    audio.current.addEventListener('canplay', canplay);
    audio.current.addEventListener('pause', pause);
    audio.current.addEventListener('seeked', seeked);
    return () => {
      audio.current.removeEventListener('canplay', canplay);
      audio.current.removeEventListener('pause', pause);
      audio.current.removeEventListener('seeked', seeked);
    };
  }, []);
  return (
    <wx-open-audio
      ref={audio}
      title="别找我麻烦"
      src="http://URL地址/.mp3"
    >
      <script type="text/wxtag-template">
        <button>播放</button>
      </script>
    </wx-open-audio>
  );
}
```

## 附录-所有开放标签列表

``` html
<wx-open-launch-weapp\>
<wx-open-launch-app\>
<wx-open-subscribe\>
<wx-open-audio\>
```

## App获取开放标签 `<wx-open-launch-app\>` 中的extinfo数据

本章节主要介绍微信内H5页面通过用户点击微信开放标签`<wx-open-launch-app>`跳转App，App侧获取该开放标签中extinfo字段的方式。

### 接入微信OpenSDK

若需要获取唤起App时携带的数据，App首先需要接入微信OpenSDK。

 **!!!注意: 本文档中所指的AppID均是指接入OpenSDK时申请的App的AppID, 非开放标签中的AppID** 

微信OpenSDK的相关文档：
**iOS**:
* [iOS OpenSDK接入文档](/doc/oplatform/Mobile_App/Access_Guide/iOS.md)
* [iOS OpenSDK资源下载](/doc/oplatform/Mobile_App/Downloads/iOS_Resource.md)

**Android**:
* [Android OpenSDK接入文档](/doc/oplatform/Mobile_App/Access_Guide/Android.md)
* [Android OpenSDK资源下载](/doc/oplatform/Mobile_App/Downloads/Android_Resource.md)

**鸿蒙**
* [鸿蒙 OpenSDK接入文档](/doc/oplatform/Mobile_App/Access_Guide/ohos.md)
* [鸿蒙 OpenSDK资源下载](/doc/oplatform/Mobile_App/Downloads/HarmonyOS_Resource.md)

### App侧获取开放标签传递的extinfo数据
**请确保已经按照上述步骤接入OpenSDK并验证成功接入**

#### iOS示例代码

重写AppDelegate.m或SceneDelegate的相关方法(参考上述步骤中的接入文档):

```bash
//AppDelegate.m
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation 
{
    //开启SDK Log
    [WXApi startLogByLevel:WXLogLevelDetail logBlock:^(NSString *log) {
        NSLog(@"WeChatSDK: %@", log);
    }];

    // 在调用WXApi的handle方法前，须先调用registerApp注册。ret为注册结果，若注册失败，请根据sdk的log排查原因
    BOOL ret = [WXApi registerApp:APP_ID universalLink:UNIVERSAL_LINK];

    if ([WXApi handleOpenURL:url delegate:[WXApiManager sharedManager]]) {
        /// handled by OpenSDK
    }
    return YES;
}

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray<id<UIUserActivityRestoring>> * __nullable restorableObjects))restorationHandler
{
    //开启SDK Log
    [WXApi startLogByLevel:WXLogLevelDetail logBlock:^(NSString *log) {
        NSLog(@"WeChatSDK: %@", log);
    }];

    // 在调用WXApi的handle方法前，须先调用registerApp注册。ret为注册结果，若注册失败，请根据sdk的log排查原因
    BOOL ret = [WXApi registerApp:APP_ID universalLink:UNIVERSAL_LINK];
    if ([WXApi handleOpenUniversalLink:userActivity delegate:[WXApiManager sharedManager]) {
        /// handled by OpenSDK
    }
    return YES;
}
```

重写WXApiDelegate的onReq方法：

```bash
/*! @brief 收到一个来自微信的请求，第三方应用程序处理完后调用sendResp向微信发送结果
 *
 * 收到一个来自微信的请求，异步处理完成后必须调用sendResp发送处理结果给微信。
 * 可能收到的请求有GetMessageFromWXReq、ShowMessageFromWXReq等。
 * @param req 具体请求内容，是自动释放的
 */
- (void)onReq:(BaseReq*)req 
{
    //获取开放标签传递的extinfo数据逻辑
	if ([req isKindOfClass:[LaunchFromWXReq class]]) 
	{
		WXMediaMessage *msg = req.message;
		NSString *openID = req.openID;
		NSString *extinfo = req.msg.messageExt;
		// handle...
	}
}
```

#### Android 示例代码

重新IWXAPIEventHandler的OnReq方法获取extInfo

```Java
public class WXEntryActivity extends Activity implements IWXAPIEventHandler{
    private IWXAPI api;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //初始化和注册
        IWXAPI api = WXAPIFactory.createWXAPI(this, Constants.APP_ID, false);
        Intent intent = getIntent();
        api.handleIntent(intent, this)
	}

    @Override
    protected void onNewIntent(Intent intent) {
		super.onNewIntent(intent);
		api.handleIntent(intent, this);
	}

	/**
	 * 从微信启动App
	 * @param req
	 */
	@Override
	public void onReq(BaseReq req) {
	   //获取开放标签传递的extinfo数据逻辑
		if(req.getType() == ConstantsAPI.COMMAND_SHOWMESSAGE_FROM_WX && req instanceof ShowMessageFromWX.Req) {
			ShowMessageFromWX.Req showReq = (ShowMessageFromWX.Req) req;
			WXMediaMessage mediaMsg = showReq.message;
			String extInfo = mediaMsg.messageExt;
			//Handle...
		}
    }

    @Override
	public void onResp(BaseResp resp) {
	}
}

```

#### 鸿蒙 示例代码
配置和微信约定的action，在App module的 `src/main/module.json5`中，找到入口ability并配置action `wxentity.action.open`:
```json
{
  "module": {
    "type": "entry",
    "abilities": [
      {
        "name": "DemoEntryAbility",
        "srcEntry": "./ets/entryability/DemoEntryAbility.ets",
        "skills": [
          {
            "entities": [
              "entity.system.home"
            ],
            "actions": [
              "action.system.home",
              "wxentity.action.open" // 微信拉起App时会用该action，需接入方配置
            ]
          }
        ]
      }
    ]
  }
}
```

重写`wxopensdk.WXApiEventHandler`的相关方法(参数上述步骤中的接入文档):
```
  /**
   * 从微信启动App
   *
   * @param req
   */
  onReq(req: wxopensdk.BaseReq): void {
    if (req instanceof wxopensdk.LaunchFromWXReq) {
      // //获取开放标签传递的extinfo数据逻辑
      let messageExt = req.message?.messageExt
      //Handle...
    }
  }
```

![](https://res8.wxqcloud.qq.com.cn/wxdoc/56617deb-b17d-4cf1-bccd-71d2c41ccc76.svg)文档变更日志（1条）
2026 年 07 月 07 日文档描述优化
