js检测自定义协议是否存在

3,577 阅读3分钟

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.踩坑记录

实际在项目实践中发现了一些问题:

  1. 有时候既打开了客户端,又执行了失败的回调。 原因:看了源码就不难发现问题。检测blur的实现方式并不是100%可靠,有可能启用本地客户端需要一段时间,有时候由于各种原因比较慢,在2s以后打开也是可能的,但是代码认为2s后就是启用客户端失败,自然会执行失败的回调。

解决方法:对于这个问题并没有比较完美的解决办法,只能把时间稍微设置的长一点,尽量避免这种情况的发生。但是这样对用户的体验不大好,用户需要等一段时间才能看到失败回调的逻辑,造成卡顿的感觉。所以这个时间要拿捏好。

  1. 对于用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下面啦,就可以使用了。

image.png

3.使用

openMultiDisplayWebview () {
  let expandScreenUrl = 'xxxxxx'
  try {
    window.protocolCheck(expandScreenUrl, function () {
      console.log('协议未注册')
      ...
    })
  } catch (e) {
   // 捕捉异常
  }
}

文章参考 JS检测自定义协议