1.自定义协议的用途
使用自定义协议可以使浏览器调起客户端本地程序,详情请点击大佬写的文章:通用URL协议实现在浏览器中打开本机任意程序。
那么在实际应用中,如何检测自定义的协议是否存在呢?ie下可以通过activex检测注册表项,chrome该怎么办呢?当然是有解决办法的,已经有人将该功能封装成了通用JS,下面来具体了解下吧。
2.源码浅析
protocolcheck.js
源码中对不同的浏览器做了兼容处理,重点在这一段:
function openUriWithTimeoutHack (uri, failCb, successCb) {
var timeout = setTimeout(function () {
failCb()
handler.remove()
}, 2000)
// handle page running in an iframe (blur must be registered with top level window)
var target = window
while (target != target.parent) {
target = target.parent
}
var handler = _registerEvent(target, 'blur', onBlur)
function onBlur () {
clearTimeout(timeout)
handler.remove()
successCb()
}
window.location = uri
}
复制代码
函数有三个传参
- uri:调用本地客户端的url
- failCb:打开客户端失败的回调
- successCb:打开客户端成功的回调
里面设置一个2秒的定时器(时间可以根据项目具体调整一下),使用window.location打开uri,因为打开uri后页面会失焦,所以可以监测window的blur事件,如果捕获到失焦,就可以认为自定义协议存在,本地客户端打开,然后调用成功的回调函数,并且清除定时器等,如果2秒后没有捕捉到页面失焦,就可以认为自定义协议不存在,打开本地客户端失败,调用失败的回调。
3.踩坑记录
实际在项目实践中发现了一些问题:
- 有时候既打开了客户端,又执行了失败的回调。
原因:看了源码就不难发现问题。检测blur的实现方式并不是100%可靠,有可能启用本地客户端需要一段时间,有时候由于各种原因比较慢,在2s以后打开也是可能的,但是代码认为2s后就是启用客户端失败,自然会执行失败的回调。
解决方法:对于这个问题并没有比较完美的解决办法,只能把时间稍微设置的长一点,尽量避免这种情况的发生。但是这样对用户的体验不大好,用户需要等一段时间才能看到失败回调的逻辑,造成卡顿的感觉。所以这个时间要拿捏好。
- 对于用window.open打开的子窗口,无法用protocolcheck.js检测到自定义协议是否存在
原因:还是从源码中找问题,注意那一行注释 handle page running in an iframe (blur must be registered with top level window)
,blur事件必须要在顶层window注册。
解决方法:我的解决方法是,不要使用window.open,而是新建一个路由,再去检测自定义协议是否存在。或者在父窗口检测完成后保存下这个状态,在子窗口中直接使用。
4.项目中使用
1.下载文件
git地址:protocolcheck.js。
我将这个js文件放在vue项目中static文件夹下了。
2.引入
添加到index.html文件中
<!DOCTYPE html>
<html>
<head>
...
<script src="/static/protocolcheck.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
复制代码
引入之后,打开控制台Console,输入window
,可以看到protocolCheck
已经挂载到window下面啦,就可以使用了。
3.使用
openMultiDisplayWebview () {
let expandScreenUrl = 'xxxxxx'
try {
window.protocolCheck(expandScreenUrl, function () {
console.log('协议未注册')
...
})
} catch (e) {
// 捕捉异常
}
}
复制代码
文章参考 JS检测自定义协议