H5 唤起 App 完整方案:Scheme、Universal Link、wx-open-launch-app 与微信兜底

3145 字
16 分钟
H5 唤起 App 完整方案:Scheme、Universal Link、wx-open-launch-app 与微信兜底

H5 唤起原生 App,是移动端前端开发最经典的难题之一。

问题不在于”怎么做”,而在于”怎么做才能在所有场景下都有效”:

  • iOS:Apple 从 iOS 9 起就废弃了 scheme 弹窗,改用 Universal Link,但配置门槛高、排查困难
  • Android:Intent 和 scheme 行为碎片化,Chrome、系统浏览器、第三方浏览器的表现各不相同
  • 微信:内置浏览器对 scheme 几乎全面拦截,Universal Link 也不生效
  • 兜底:唤起失败后,怎么判断?怎么引导用户?怎么跳转到应用商店?

这篇文章给出一个经过生产验证的组合方案:两套并行,互为主备。系统浏览器走 Scheme / Universal Link,微信内走 wx-open-launch-app + 右上角浏览器引导兜底。

一、核心思路#

flowchart TD A[用户打开 H5 页面] --> B{判断运行环境} B -- 微信内置浏览器 --> C[wx-open-launch-app] B -- 系统浏览器 --> D[iOS: Universal Link\nAndroid: Intent/Scheme] C --> E{唤起成功?} E -- 否 --> F[浏览器引导蒙层] F --> G[系统浏览器再走 Scheme/UL] E -- 是 --> H[进入 App] D --> I{唤起成功?} I -- 是 --> H I -- 否 --> J[应用商店/下载页] G --> K{成功?} K -- 是 --> H K -- 否 --> J

为什么是两套而不是一套?

微信内置浏览器对 scheme 几乎全面拦截。用 iframe 或 location.href 尝试唤起,成功率极低。微信官方提供的唯一可靠方案是 wx-open-launch-app 开放标签,但它需要公众号 + 开放平台移动应用 + JS-SDK 签名,门槛不低。

所以最优策略是:微信内优先用 wx-open-launch-app,失败后引导用户到系统浏览器;系统浏览器里用 Scheme / Universal Link 唤起,失败后跳应用商店。

二、方案一:Scheme / Universal Link(系统浏览器为主)#

2.1 总览流程#

flowchart TD A[用户打开 H5 页面] --> B[点击「打开 App」按钮] B --> C{当前环境?} C -- 微信内置浏览器 --> D[微信会拦截大部分 scheme/UL] D --> E[仅用 iframe 或 location 尝试 scheme] E --> F{2~3 秒内页面是否 hidden?\nvisibilitychange / pagehide} F -- 是 --> G[认为唤起成功 → App 已打开] F -- 否 --> H[唤起失败] H --> I[显示兜底:右上角「在浏览器中打开」蒙层] I --> J[用户切到 Safari / Chrome] J --> K[走系统浏览器唤起流程] C -- 系统浏览器 --> K K --> L{系统类型?} L -- iOS --> M[优先 Universal Link\nhttps://你的域名/...] L -- Android --> N[Intent 链接 或 scheme\nyourapp://...] L -- 其他 --> O[直接 scheme\nyourapp://...] M --> P{2~3 秒内页面是否 hidden?} N --> P O --> P P -- 是 --> G P -- 否 --> Q[认为未安装或唤起失败] Q --> R[跳转应用商店 / 下载页] Q --> S[或继续留在 H5 页面]

Universal Link 是 Apple 官方推荐的 H5 → App 唤起方式。它的工作原理是:浏览器访问一个 https 链接,系统自动识别该链接是否被某个已安装的 App 注册,如果匹配,直接打开 App 而不是继续加载网页。

flowchart TD A[iOS Safari 点击按钮] --> B["location.href = https://m.xxx.com/deep/123"] B --> C{系统是否识别 Universal Link?} C -- 已安装 App 且 UL 配置正确 --> D[直接打开 App\n带上页面参数] C -- 未安装 App --> E[浏览器继续打开 https 页面\n即 H5 落地页] C -- UL 配置错误或域名不匹配 --> F[只在浏览器打开 H5\n无法进 App] D --> G[App 解析参数\n跳转对应业务页] E --> H[H5 显示「去下载 App」] F --> H

iOS 必备配置:

配置项说明关键细节
服务器部署 apple-app-site-associationJSON 文件,无后缀,HTTPS,根目录或 .well-known/
XcodeAssociated Domains → applinks:你的域名不需要 https:// 前缀
链接使用 https:// 同一域名下的路径路径需匹配 AASA 文件中的 paths

AASA 文件示例:

{
"applinks": {
"details": [{
"appID": "TEAM_ID.com.your.app",
"paths": ["/deep/*", "/app/*"]
}]
}
}

常见坑:AASA 文件必须返回 Content-Type: application/json,不能带 .json 后缀。用 https://域名/apple-app-site-association 直接访问,应该返回 JSON 而不是 404。

2.3 Android:Intent / Scheme 细节#

flowchart TD A[Android Chrome 点击按钮] --> B{使用哪种方式?} B -- Intent --> C["intent://path#Intent;\nscheme=yourapp;\npackage=com.xxx.app;end"] B -- Scheme --> D["location.href = yourapp://path?id=123"] C --> E{是否已安装目标 App?} D --> E E -- 是 --> F[打开 App 对应页面] E -- 否 --> G[Intent 可能跳应用市场\nscheme 可能无反应] G --> H[2~3 秒后页面仍在前台] H --> I[跳转应用商店 / 下载页]

Intent 链接格式:

const intentUrl = 'intent://path#Intent;' +
'scheme=yourapp;' +
'package=com.xxx.app;' +
'S.browser_fallback_url=https://xxx.com/download;end';
location.href = intentUrl;

Intent 的优势是自带 browser_fallback_url,未安装时自动跳转到指定页面(通常是下载页)。但部分定制 ROM 浏览器对此支持不一致。

纯 Scheme 格式:

location.href = 'yourapp://path?id=123';

Scheme 的缺点是:未安装时,部分浏览器会弹”找不到应用”错误框,体验较差。

2.4 成功/失败判断逻辑#

这是整个方案的核心——怎么知道唤起成功了还是失败了?

flowchart TD A[执行唤起\nscheme / Universal Link / Intent] --> B[开始计时 2500ms] B --> C[监听 visibilitychange / pagehide] C --> D{计时结束前 document.hidden?} D -- 是 --> E[判定:可能已成功打开 App\n不再执行兜底] D -- 否 --> F[判定:唤起失败或未安装] F --> G[跳转应用商店] F --> H[微信内:显示「浏览器打开」蒙层]

核心原理: 当 App 被唤起时,浏览器页面会被切到后台,document.hidden 变为 true,同时触发 visibilitychangepagehide 事件。如果在计时窗口内检测到页面隐藏,就判定为唤起成功。

代码实现:

function openApp() {
var scheme = 'yourapp://page?id=123';
var universal = 'https://m.xxx.com/app/page?id=123';
var hidden = false;
document.addEventListener('visibilitychange', function () {
if (document.hidden) hidden = true;
});
if (isWeChat()) {
// 微信内:尝试 iframe 唤起,2.5秒后判断
tryIframeScheme(scheme);
setTimeout(function () {
if (!hidden) showBrowserGuide();
}, 2500);
return;
}
// 系统浏览器
if (isIOS()) {
location.href = universal; // iOS 优先 UL
} else {
location.href = scheme; // Android 用 scheme
}
setTimeout(function () {
if (!hidden) location.href = APP_STORE_URL; // 兜底跳商店
}, 2500);
}

2.5 微信 vs 系统浏览器 对照#

flowchart TD A[用户点击「打开 App」] --> B{当前在什么环境?} B -->|微信内置浏览器| C[iframe / scheme 尝试唤起] C --> D{2.5s 内页面是否切到后台?} D -->|是| OK([进入 App]) D -->|否| E[显示右上角「在浏览器中打开」蒙层] E --> F[用户切到 Safari / Chrome 打开同一 H5] F --> G B -->|系统浏览器| G[执行唤起] G --> H{手机系统?} H -->|iOS| I[Universal Link\nhttps://你的域名/...] H -->|Android| J[Intent 或 scheme\nyourapp://...] H -->|其他| K[scheme\nyourapp://...] I --> L{2.5s 内页面是否切到后台?} J --> L K --> L L -->|是| OK L -->|否| M[跳转应用商店\n或继续浏览 H5]
环境主路径失败兜底
微信内iframe / scheme(多数被拦)右上角浏览器引导 → 切系统浏览器
系统浏览器 iOSUniversal Link应用商店
系统浏览器 AndroidIntent / scheme应用商店

三、方案二:wx-open-launch-app + 浏览器引导兜底(微信内为主)#

3.1 总览流程#

flowchart TD A[用户在微信内打开 H5] --> B[前端请求后端签名] B --> C["wx.config({ openTagList: ['wx-open-launch-app'] })"] C --> D{wx.config 成功?} D -- 否 --> E[兜底:显示右上角「在浏览器中打开」蒙层] E --> F[可选:复制链接 / 应用商店] D -- 是 --> G[展示 wx-open-launch-app 按钮] G --> H[用户点击「打开 App」] H --> I[微信调起 App] I --> J{触发 launch 还是 error?} J -- launch 成功 --> K[App 打开成功] J -- error 失败 --> E E --> L[用户:右上角 ⋯ → 在浏览器中打开] L --> M[系统浏览器打开同一 H5] M --> N[走 Scheme / Universal Link 流程] N --> O{唤起成功?} O -- 是 --> K O -- 否 --> F

3.2 前置条件#

使用 wx-open-launch-app 需要满足以下所有条件,缺一不可:

序号条件说明
1微信开放平台移动应用appid移动应用 AppID,不是公众号 AppID
2公众号 + JS 安全域名后端出签名,前端 wx.config
3移动应用与公众号绑定开放平台里完成绑定
4H5 部署 https本地 file:// 无效
flowchart LR A[微信开放平台\n移动应用 AppID] --> D[wx-open-launch-app] B[微信公众平台\n认证服务号] --> C[wx.config 签名] C --> D D --> E[移动应用与公众号绑定] E --> F[JS 接口安全域名] F --> G[H5 必须 https]

3.3 时序图:从签名到唤起#

sequenceDiagram participant U as 用户 participant H5 as H5 页面 participant API as 后端签名接口 participant WX as 微信客户端 participant App as 原生 App U->>H5: 在微信内打开页面 H5->>API: GET /jssdk-sign?url=当前页 API-->>H5: appId/timestamp/nonceStr/signature H5->>WX: wx.config + openTagList WX-->>H5: config 成功 H5->>U: 显示 wx-open-launch-app 按钮 U->>H5: 点击按钮 H5->>WX: 开放标签 launch WX->>App: 调起 App(extinfo 传参) App-->>U: 打开对应页面

3.4 失败兜底:右上角浏览器引导#

flowchart TD A[唤起失败触发点] --> B{哪种失败?} B -- wx.config 失败 --> C[showBrowserGuide] B -- launch error 事件 --> C B -- 开放标签未渲染 --> C B -- 用户点「无法打开?」 --> C C --> D[全屏蒙层] D --> E[箭头指向右上角 ⋯] E --> F[文案:在浏览器中打开] F --> G[可选:复制链接按钮] G --> H[用户切到 Safari / Chrome] H --> I[同一 H5 走 scheme / UL 唤起] I --> J{成功?} J -- 是 --> K[进 App] J -- 否 --> L[应用商店 / 继续 H5]

什么时候显示兜底 UI?

时机是否显示浏览器引导
wx.config 失败✅ 立刻显示
开放标签 error 事件✅ 立刻显示
开放标签未渲染✅ 立刻显示
用户点「无法打开?」✅ 手动显示
launch 成功❌ 不显示
系统浏览器环境❌ 用 scheme/UL,不用开放标签

兜底 UI 结构:

┌─────────────────────────────────────┐
│ ↗ 箭头 │
│ │
│ 请在浏览器中打开 │
│ │
│ ① 点右上角 ⋯ │
│ ② 选「在浏览器中打开」 │
│ ③ 再点「打开 App」 │
│ │
│ [ 复制链接 ] [ 去下载 App ] │
│ │
│ 点击空白处关闭 │
└─────────────────────────────────────┘

3.5 代码实现#

开放标签 HTML:

<wx-open-launch-app
id="launch-app"
appid="wx_移动应用appid"
extinfo="goods_id=123"
>
<template>
<style>.btn {
display:block; width:100%; height:48px;
background:#e02e24; color:#fff;
border:none; border-radius:8px;
}</style>
<button class="btn">打开 App</button>
</template>
</wx-open-launch-app>

JS 配置与事件监听:

wx.config({
appId: 'wx_公众号appid',
timestamp, nonceStr, signature,
jsApiList: [],
openTagList: ['wx-open-launch-app']
});
document.getElementById('launch-app')
.addEventListener('error', function () {
showBrowserGuide();
});

兜底蒙层:

<div id="browser-guide" style="display:none;
position:fixed; inset:0;
background:rgba(0,0,0,.88); z-index:9999;
color:#fff; padding:24px;">
<div style="text-align:right; font-size:48px;">↗</div>
<h2>请在浏览器中打开</h2>
<p>① 点右上角 <strong>⋯</strong></p>
<p>② 选择 <strong>在浏览器中打开</strong></p>
<p>③ 再点击 <strong>打开 App</strong></p>
</div>

四、生产环境推荐组合#

flowchart TD A[用户打开 H5] --> B{是否在微信内?} B -- 是 --> C[wx-open-launch-app 主按钮] C --> D{launch 成功?} D -- 是 --> E[进 App] D -- 否 --> F[浏览器引导蒙层] F --> G[系统浏览器] G --> H[scheme / Universal Link] H --> I{成功?} I -- 是 --> E I -- 否 --> J[应用商店] B -- 否 --> H

五、两套方案对比#

对比项Scheme / Universal Linkwx-open-launch-app + 兜底
适用环境系统浏览器(主)微信内置浏览器(主)
是否需要开放平台(移动应用 + 绑定公众号)
是否需要公众号(JS-SDK 签名)
微信内直接唤起❌ 多数被拦截✅ 用户点按钮可唤起
失败兜底右上角浏览器引导 + 应用商店同上 + 复制链接
实现复杂度中高

六、检查清单#

  • iOS:apple-app-site-association 已部署,无后缀,Content-Type 正确
  • iOS:Xcode Associated Domains 已配置
  • Android:Intent-filter / scheme 已在 AndroidManifest 注册
  • H5:不跳转易白屏的外链(如部分短链域名)
  • 监听 visibilitychange 做失败判断
  • 微信内:准备浏览器引导蒙层

wx-open-launch-app#

  • 开放平台移动应用已创建并审核通过
  • 公众号已认证,JS 安全域名已配置
  • 移动应用与公众号已在开放平台绑定
  • wx.configopenTagList: ['wx-open-launch-app']
  • 开放标签 appid移动应用 AppID(不是公众号)
  • 监听 error 事件 → showBrowserGuide()
  • 系统浏览器备用 scheme / UL 流程已就绪

七、总结#

  • 核心难点:微信拦截 scheme、iOS UL 配置繁琐、Android 碎片化
  • 方案一:Scheme / Universal Link,适合系统浏览器,实现简单
  • 方案二:wx-open-launch-app,适合微信内,需要开放平台 + 公众号 + 签名
  • 推荐组合:微信内优先 wx-open-launch-app,失败后引导到系统浏览器走 Scheme/UL
  • 成功判断visibilitychange + 2.5 秒超时,页面 hidden = 成功
  • 兜底策略:右上角浏览器引导 → 系统浏览器 → 应用商店

H5 唤起 App 没有银弹。正确的做法是分层:微信内用微信的能力,浏览器用系统的能力,每一层都准备好失败的退路。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

H5 唤起 App 完整方案:Scheme、Universal Link、wx-open-launch-app 与微信兜底
https://boke.hackerdream.xyz/posts/h5-open-app-scheme-weixin-universal-link/
作者
晴天
发布于
2026-06-01
许可协议
CC BY-NC-SA 4.0
相关文章 智能推荐
1
前端 Observer API 全家桶:Intersection、Resize 与 MutationObserver 深度实战
浏览器与性能 从"DOM 变了怎么办"出发,系统讲清 IntersectionObserver、ResizeObserver、MutationObserver 三大原生 API 的工作机制、踩坑点与组合实战,并给出一套可复用的生产级懒加载 + 无限滚动 + 自适应布局 SDK。
2
前端 CORS 跨域与 Cookie 策略:SameSite、跨域携带凭证实战
浏览器与安全 深入解析 CORS 跨域资源共享机制与 Cookie 安全策略,从 SameSite 属性到 credentials 跨域携带,涵盖预检请求、凭证模式、常见死锁与调试指南,附完整前后端代码示例。
3
CSS Subgrid 深度实战:告别嵌套布局的对齐噩梦
前端架构 全面解析 CSS Subgrid 的核心原理与实战技巧,通过卡片列表、表单、仪表盘等真实场景演示如何用 subgrid 优雅解决嵌套网格对齐难题,附浏览器兼容方案。
4
这个 GitHub 爆火的 WCAG 无障碍 Skill 到底能干嘛?实测来了
AI工具 16.7 万星项目 everything-claude-code 中的无障碍(Accessibility)Skill,教你让网站和 App 对所有用户友好,覆盖 Web/iOS/Android 三端,含完整代码示例和最佳实践清单。
5
AI 函数调用与 MCP 协议深度解析:从 OpenAI tools 到 Model Context Protocol
AI 系统拆解 AI 函数调用(Function Calling/Tool Use)与 Model Context Protocol(MCP)的来龙去脉。从 OpenAI tools 协议栈、Claude Tool Use、Gemini Function Calling 的差异,到 MCP 协议的 JSON-RPC 通信、Resources/Prompts/Tools/Sampling 四大原语与 stdio/SSE/HTTP 三种传输,再到自建 MCP Server 接入 Claude Desktop 与 Claude Agent SDK 的全链路实战,附 6 个完整可运行示例。
随机文章 随机推荐
Profile Image of the Author
晴天
Hello, I'm 晴天.
公告
欢迎来到我的博客!这是一则示例公告。
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
155
分类
24
标签
387
总字数
345,424
运行时长
0
最后活动
0 天前

目录