Electron伪协议参数获取

上一期我们介绍了如何设置伪协议,并通过伪协议链接拉起客户端,其实伪协议还有很多场景的,比如百度网盘或者迅雷,点击网页上的链接进行下载,或者点击链接拉起客户端后打开某个页面,其实这都是可以用伪协议链接实现的,链接中的参数就和网页的参数没啥区别,本期就介绍如何获取伪协议链接中的参数。

实现目标

  1. 软件关闭时通过伪协议拉起软件获取该伪协议链接。
  2. 软件启动时通过伪协议拉起软件获取该伪协议链接。
  3. 通过伪协议传入一个图片链接和router path路径,跳转该路径,并下载图片本地加载显示。

伪协议获取差异

上一章我们介绍了伪协议是怎么启动客户端的,即:

  1. Windows是通过注册表写入伪协议,调用应用启动exe和%1启动客户端的。
  2. Mac是通过软件包中的info.plist文件设置启动客户端的。

这两者的启动方式不同,故获取方式也是不同的,我们来看一下通过伪协议的启动流程吧:

Windows伪协议启处理

  1. 首先软件处于关闭状态的话,我们在浏览器上打开伪协议链接,会注册表上去找对应的伪协议,启动exe并传入伪协议链接(%1),也就是我们实际上打开的是xxx.exe vue-cli-electron://xxxx
    此时可以直接通过process.argv来获取这个伪协议链接,之前说了process.argv这个是数组,里面是Electron的启动参数,如果我们是通过伪协议拉起软件的话,数组最后一项就是我们的伪协议链接
process.argv:
[
  xxx.exe,
  .....,
  vue-cli-electron://xxxx
]

那么此时伪协议为process.argv[process.argv.length - 1]

  1. 软件处于开启状态的话,我们可以通过app的second-instance事件来获取这个argv,second-instance当第二个实例被执行并且调用app.requestSingleInstanceLock()时触发的,解释一下就是我们的软件正在运行,伪协议触发打开exe,我们主进程中调用了app.requestSingleInstanceLock()的话,second-instance就会触发,直观表现为伪协议打开软件,触发second-instance事件,我们可以在这个里面拿到argv。
import { app } from 'electron'
import global from '../config/global'
const gotTheLock = app.requestSingleInstanceLock()

export default function() {
  // 点击图标启动时检测窗口是否存在,存在则打开
  if (!gotTheLock) {
    app.quit()
  } else {
    app.on('second-instance', (event, argv) => {
      console.log(argv[argv.length - 1])
      const win = global.sharedObject.win
      if (win) {
        if (win.isMinimized()) win.restore()
        if (win.isVisible()) {
          win.focus()
        } else {
          win.show()
          win.setSkipTaskbar(false)
        }
      }
    })
  }
}

Mac伪协议处理

Mac软件的启动就没啥参数可带的了,所以process.argv这个东西没啥用,那么如何获取呢,看看官方描述链接

在 macOS 上, 当用户尝试在 Finder 中打开您的应用程序的第二个实例时, 系统会通过发出 open-file 和 open-url 事件来自动强制执行单个实例,。 但是当用户在命令行中启动应用程序时, 系统的单实例机制将被绕过, 您必须手动调用此方法来确保单实例。

故我们可以通过open-url来获取我们的伪协议链接,注:Mac系统无论软件启动或没启动都是通过open-url来拿取的

app.on('open-url', (_event, urlStr) => {
  console.log(urlStr)
})

具体实现

winSingle

import { app } from 'electron'
import global from '../config/global'
const gotTheLock = app.requestSingleInstanceLock()

export default function() {
  // 点击图标启动时检测窗口是否存在,存在则打开
  if (!gotTheLock) {
    app.quit()
  } else {
    // 这里是软件在启动时,伪协议打开触发的
    app.on('second-instance', (event, argv) => {
      console.log(argv)
      const win = global.sharedObject.win
      // 直接把伪协议链接发送给渲染进程
      win.webContents.send('renderer-scheme', argv[argv.length - 1])
      if (win) {
        if (win.isMinimized()) win.restore()
        if (win.isVisible()) {
          win.focus()
        } else {
          win.show()
          win.setSkipTaskbar(false)
        }
      }
    })
  }
}

主进程

import winSingle from './services/winSingle'

winSingle()

app.isReady() ? onAppReady() : app.on('ready', onAppReady)

async function onAppReady() {
  if (!process.env.WEBPACK_DEV_SERVER_URL) {
    createProtocol('app')
  }
  // 这里怎么处理看个人喜好,你可以启动时就根据scheme打开对应的窗口,或者是在窗口加载完成后发个通知给渲染进程,渲染进程进行重定向。
  // if (process.argv.length > (app.isPackaged ? 1 : 2)) {
  //   const scheme = process.argv[process.argv.length - 1]
  //   处理scheme省略
  //   initWindow('#xxxpath')
  // } else {
  //   initWindow('')
  // }
  initWindow('')
  // 这里的逻辑是win关闭通过伪协议拉起软件,由于我们要通知渲染进程,所以得在渲染进程加载完毕后通知
  win.webContents.once('did-finish-load', () => {
    // 直接打开软件的话开发环境的启动参数为2,安装包为1,大于这个数的话说明是通过伪协议拉起软件的
    if (process.argv.length > (app.isPackaged ? 1 : 2)) {
      // 我们这里主动触发'second-instance',传入process.argv,在那边统一处理了
      app.emit('second-instance', null, process.argv)
    }
  })
}
// mac伪协议链接就是urlStr
app.on('open-url', (_event, urlStr) => {
  console.log(urlStr)
  if (win) {
    // 这里是mac软件打开时使用伪协议打开软件
    win.webContents.send('renderer-scheme', urlStr)
    if (win.isMinimized()) win.restore()
    if (win.isVisible()) {
      win.focus()
    } else {
      win.show()
      win.setSkipTaskbar(false)
    }
  } else {
    // 这里是mac软件关闭时使用伪协议打开软件,如果你中间process.argv没有改变可以这样写,会继续走上面win的did-finish-load的流程
    // 如果有改变的话老老实实写个did-finish-load进行win.webContents.send('renderer-scheme', urlStr)推送
    process.argv.push(urlStr)
  }
})

我这里是主进程获取到伪协议链接,然后发通知给渲染进程的,一些不懂的地方请看上面注释解释,简单说一下就是:

  • open-url里处理Mac的伪协议,根据win的值来区分软件是处于开启还是关闭。
  • ready里拿到伪协议的是软件处于关闭状态的,second-instance里软件是启动状态。
  • Mac软件关闭状态启动的话,argv里只有启动路径一项,不会走argv.length > 1这边的逻辑的,但是open-url会比ready先触发,我们可以在这个里面进行手动argv.push(伪协议链接)让其走Win软件关闭启动的逻辑。

渲染进程处理

这里我们使用的伪协议链接为vue-cli-electron:///file/localFile?image=https://xuxinblog.oss-cn-qingdao.aliyuncs.com/blog/2021/07/01/1.jpg,拿到的也是这个完整链接,我们处理一下这个链接,跳转到/file/localFile,并下载query参数里面的image图片地址。下载及显示逻辑用之前的加载本地文件的逻辑。

App.vue
onMounted(() => {
  window.ipcRenderer.on('renderer-scheme', (_event, data) => {
    console.log(data)
    const urlObj = new URL(data)
    const query = {}
    urlObj.search.slice(1).split('&').forEach(s => {
      const item = s.split('=')
      query[item[0]] = item[1]
    })
    router.replace({
      path: urlObj.pathname.slice(2),
      query
    })
  })
})
onUnmounted(() => {
  window.ipcRenderer.removeAllListeners('renderer-scheme')
})
localFile.vue
onMounted(() => {
  const localImage = LgetItem('localImage')
  if (route.query.image) {
    download(route.query.image)
  } else {
    if (localImage) {
      state.image = localImage
    }
  }
})

好了,接下来使用浏览器打开vue-cli-electron:///file/localFile?image=https://xuxinblog.oss-cn-qingdao.aliyuncs.com/blog/2021/07/01/1.jpg试试,看看拉起软件后是否跳转到本地文件的路由,且显示的是伪协议中image的图片。

本系列更新只有利用周末和下班时间整理,比较多的内容的话更新会比较慢,希望能对你有所帮助,请多多star或点赞收藏支持一下

本文地址:https://xuxin123.com/electron/url-scheme-query

本文github地址:链接

You may also like...

1 Response

  1. Trojanking说道:

    你好我想问下,前端调用伪协议后,能获得伪协议的返回值吗,或者说能想http那样吗

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注