
sunilwang
2023/04/04阅读:94主题:绿意
移动端 H5 页面唤起 APP 方案
移动端 H5 页面唤起 APP 方案
移动端 H5 页面通常承担的主要责任就是引流,利用 Web 页面轻量便于传播的特性通过搜索引擎或者社交软件进行传播,然后在页面内引导用户打开 APP 进行消费,引流主要有两种形式:
-
拉活:引导已安装 APP 用户打开 APP 进行消费,提升用户粘性和 APP 日活数据。 -
拉新:引导未安装 APP 用户安装 APP,增加 APP 的用户量。
H5 页面唤起 APP 的主要技术方案有以下三种:
-
方案一:URL Scheme(通用) -
方案二:Universal Link(IOS) -
方案三:wx-open-launch-app(微信)
方案一和方案二属于系统级别解决方案,方案三是在微信内的解决方案,虽然不是一个级别但是重要性却一点都不低。
URL Scheme 方案
手机系统中有很多私密信息,为了保证信息的安全,系统设置沙箱机制,应用只能访问自己沙箱内的资源。沙箱机制保证了数据的安全,同时也阻碍了应用直接的信息共享,URL Scheme 是一种页面跳转协议,跳转同时可以通过参数传递数据。利用这一特性可以实现 H5 页面呼起 APP 功能。
URL Scheme 一般由协议名、路径、参数组,模式如下:[scheme:][//authority][path][?query][#fragment]
。APP 安装后会向操作系统注册一个 Scheme,操作系统拦截到请求就会调起匹配的程序进行处理。
我们常规的请求发送基本都可以用来触发 APP Scheme,例:
-
<a>
标签链接跳转 -
直接通过 window.location.href
触发 -
通过 iframe
触发
例:唤起通话呼叫 10086window.location.href = 'tel:10086';
URL Scheme 方案优点:
-
兼容性好,Android 和 IOS 系统都支持 -
不要求用户主动触发,可以使用 JS 触发
URL Scheme 方案缺点:
-
体验问题,需要用户二次确认,呼起失败处理有延迟 -
容易被拦截,在微信、百度等 APP 主要浏览入口都有白名单机制,非白名单内的 Scheme 都会被拦截无法呼起 APP
体验问题一:使用 URL Scheme 呼起 APP 系统会弹出确认框要求用户进行二次确认

体验问题二:使用 URL Scheme 呼起 APP,唤起失败处理有延迟
Web 页面无法判断用户是否安装 APP 也无法感知用户的二次确认是否通过,通常我们都希望引导未安装 APP 用户到 APP 下载页面安装 APP。因为无法感知用户是否安装 APP 和用户的二次确认结果,通常的解决方案是通过visibilitychange
配合定时器来判断,在 APP 呼起触发一段时间后如果页面仍是可见状态,那么就认为 APP 呼起失败,失败后通常会引导用户到下载页。
未安装用户唤起效果:
用户不允许唤起 APP 效果:
核心代码:
let timer = null;
const jumpAppInH5 = () => {
// 呼起APP
window.location.href = 'APP Scheme 地址';
// 3s之后跳转到APP下载页
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
window.location.href = 'APP下载页面地址';
}, 3000);
// 跳转app之后禁止再跳转中间页
document.addEventListener('visibilitychange', () => {
clearTimeout(timer);
});
};
体验问题三:呼起 APP 二次弹窗,用户长时间未进行确认,这时即使用户同意打开 APP 页面也仍然会任务呼起失败跳转到 APP 下载页。
用户长时间未确认示例:
产生这个问题的原因是浏览器的 Event Loop 机制,二次确认弹窗虽然阻塞主进程,但是定时器并不在主进程执行,定时器执行完成之后会将回调函数添加到任务队列,一旦用户进行二次确认主进程在空闲后会立即处理任务队列中的任务跳转到 APP 下载页。
Universal Link 方案
Universal Link 是 IOS9(2015 年 6 月发布) 中新增的功。它可允许接通过 https 协议的链接来打开 APP。
和 URL Scheme 方案类似,在 APP 中注册自己要支持的域名,系统拦截到请求后会直接呼起匹配的 APP,如果没有匹配的 APP 则直接使用浏览器打开。
优点:从系统层面用户体验更好
-
唤端时没有弹窗提示是否打开,提升用户体验 -
对未安装 APP 的用户也会使用浏览器打开页面,不影响用户浏览体验
缺点:
-
只支持 IOS 系统 -
需要用户主动触发(例如点击),不能通过 JS 触发 -
不能实现呼端失败后引导到 APP 下载页面,因为用户触发后一定会跳离当前页面
Android 提出了类似 Universal Link 的 App Link 方案 和 Chrome Intents 方案,在国内支持情况较差应用较少,感兴趣可以自行查找资料了解。
wx-open-launch-app 方案
因为微信的白名单机制 URL Scheme 唤起请求会被拦截,但是微信在国内环境是绝对的头部流量入口,是不能够放弃的。微信提供开放标签wx-open-launch-app
方案可以实现在微信内通过 H5 唤起 APP。详见开放标签说明文档
微信开放标签唤起 APP 示例:
使用wx-open-launch-app
需要满足两个前置条件:
-
条件一:页面接入 JS-SDK -
步骤一:域名绑定 -
配置入口:微信公众平台-设置与开发-公众号设置-功能设置-JS 接口安全域名 -
需要在配置的域名服务器部署平台提供的验证文件
-
-
步骤二:引入 JS-SDK 文件,在页面使用 SDK 之前需要先引入 -
步骤三:通过 wx.config
接口注入权限验证配置-
注意使用前端路径实现的 SPA 应用需要在 URL 变化后重新验证配置 -
权限验证需要签名,需要后端能力
-
-
监听 wx.ready()
和wx.error()
回调实现后续业务功能
-
-
条件二:App 必须接入微信 OpenSDK,详见接入指南
微信开放标签wx-open-launch-app
核心代码
<script src="//res2.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<script>
wx.config({
debug: false, // 开启调试模式,调用的所有 api 的返回值会在客户端 alert 出来,若要查看传入的参数,可以在 pc 端打开,参数信息会通过 log 打出,仅在 pc 端时才会打印。
appId: "xxxxxxxxxxxx",
timestamp, // 必填,生成签名的时间戳
nonceStr, // 必填,生成签名的随机串
signature, // 必填,签名
jsApiList: ["onMenuShareTimeline", "onMenuShareAppMessage"], // 注册的方法
openTagList: ["wx-open-launch-app", "wx-open-launch-weapp"], // 注册的开放标签
});
</script>
<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>
// 监听APP拉取回调,实现业务逻辑处理
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-launch-app
使用注意事项:
-
必须是通过卡片形式进入页面的页面才会生效 -
SPA 页面路径:调用 wx.config
注入配置信息只在当前页面生效,同一个 url 仅需调用一次,对于变化 url 的 SPA 的 web app 需要在每次 url 变化时进行调用 -
开放标签依赖 Web Components 方案,极少部分 Android 系统可能由于版本太低而不支持
58 到家工作端 APP 唤醒方案
58 到家工作端主要采用 URL Scheme 和wx-open-launch-app
方案结合实现客户端呼起功。 58 到家工作端的渠道投放渠道是通过微信和手机短信,需要同时支持浏览器和微信环境内 APP 呼起,APP 唤起失败需要自动跳转到 APP 下载页面。
我们对页面运行环境检测、微信开放标签初始化和唤起失败处理等非业务逻辑进行封装,简化日常业务逻辑开发。 组件使用插槽和高阶组件的思想封装,自动判断当前运行环境调用合适的方式进行客户端唤起,组件名称OpenLaunchApp
。
组件使用(Vue):
<OpenLaunchApp :launchHandler="handleOpenAppClick">
<div class="btn-open-app" @click="handleOpenAppClick">打开APP查看更多</div>
</OpenLaunchApp>
<script>
export default {
methods: {
// 呼起回调,可以用来处理数据上报等逻辑
handleOpenAppClick() {}
}
}
</script>
<style>
.btn-open-app {
width: 100%;
height: 90px;
margin: 15px 0px 15px;
line-height: 90px;
border-radius: 12px;
font-size: 32px;
font-weight: 500;
text-align: center;
position: relative;
color: rgba(255, 255, 255, 1);
background-color: rgba(0, 195, 168, 1);
}
</style>
组件核心代码(Vue):
<template>
<div class="root-wx-launch" v-if="isAvailLaunchApp && initWxConfigSuccess">
<!-- 使用插槽 插入按钮 -->
<slot />
<wx-open-launch-app
class="launch-btn"
appid="xxxxxxxxxxxxxxx"
@ready="handleReady"
@error="handleError"
@launch="handleLaunch"
:extinfo="extinfo"
>
<component v-bind:is="'script'" type="text/wxtag-template">
<!-- 元素必须有尺寸,否则无法触发呼端,但不要求元素被点击才能触发 -->
<div class="wx-btn" style="width: 100%; height: 50px"></div>
</component>
</wx-open-launch-app>
</div>
<div v-else @click="jumpAppInH5"><slot /></div>
</template>
<script setup>
import { ref } from 'vue';
import { isAvailLaunchApp as isAvailLaunchAppCheck } from '@/utils/osUtil';
import launchAppUtil from '@/utils/launchAppUtil';
const props = defineProps({
launchUrl: {
type: String,
required: false,
default: window.location.href,
},
// 呼端成功回调,可以用啦处理埋点等需求
launchHandler: {
type: Function,
required: false,
},
});
const isAvailLaunchApp = isAvailLaunchAppCheck();
const initWxConfigSuccess = ref(true);
const {
extinfo,
handleReady,
jumpAppInH5,
handleDownloadApp,
handleError: defaultErrorHandler,
} = launchAppUtil(
props.launchUrl,
() => {
initWxConfigSuccess.value = false;
},
// 避免SPA前端路由调整导致微信配置验证失败
location.href.split('#')[0],
);
const handleLaunch = (e) => {
console.log('launch success', e.detail);
if (typeof props.launchHandler === 'function') {
props.launchHandler();
}
};
const handleError = (error) => {
handleDownloadApp();
};
</script>
<style scoped>
/* 根元素相对定位,根元素尺寸又通过插槽传入的元素撑起 */
.root-wx-launch {
position: relative;
}
/* 通过绝对定位,使wx-open-launch-app尺寸覆盖插入元素 */
.launch-btn {
position: absolute;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
z-index: 99;
overflow: hidden;
}
</style>
作者介绍
乔学伟:传说中的新冠无症状
作者介绍
