electron 开发经验之谈系列-electron 自定义协议

本文最后更新于:2021年3月22日 早上

场景

有时候需要与其他程序进行交互时,自定义协议是一个不错的选择 – 它能在程序为启动时启动程序然后处理其它程序的动作,而这是其它解决方案,包括 HTTP 请求、共享数据库不能比的。其实日常生活中也有现成的例子,迅雷的自定义协议下载链接、BitTorrent 协议、百度网盘启动本地客户端等等。

使用

参考: 将当前可执行文件设置为协议的默认处理程序(注册表级别)

  1. 让程序保持单例启动
  2. 设置客户端支持的协议(在 Windows 中会写入到注册表)
  3. 处理命令行参数找到其中需要的 url 信息
  4. 监听 readysecond-instance 事件

让程序保持单例启动

参考: app.requestSingleInstanceLock()
注: 仅在单例模式下才能监听 second-instance 事件

// 请求单例锁,避免打开多个 electron 实例
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
  app.quit();
  return;
}

设置客户端支持的协议(在 Windows 中会写入到注册表)

参考: app.setAsDefaultProtocolClient(protocol[, path, args])

import { app } from "electron";
import path = require("path");

/**
 * 客户端默认支持的协议
 */
export class DefaultProtocolClient {
  constructor(public readonly protocol: string) {}

  /**
   * 注册一个默认支持打开的协议
   */
  register() {
    // 开发模式下在 window 运行需要做兼容
    if (
      process.env.NODE_ENV === "development" &&
      process.platform === "win32"
    ) {
      // 设置 electron.exe 和 app 的路径
      app.setAsDefaultProtocolClient(this.protocol, process.execPath, [
        path.resolve(process.argv[1]),
      ]);
    } else {
      app.setAsDefaultProtocolClient(this.protocol);
    }
  }

  /**
   * 从命令行参数中找到 url
   * @param argv
   */
  findUrl(argv: string[]): string | undefined {
    const regExp = new RegExp(`^${this.protocol}://`);
    return argv.find((str) => regExp.test(str));
  }
}

const defaultProtocolClient = new DefaultProtocolClient("custom-protocol");

await defaultProtocolClient.register();

处理命令行参数找到其中需要的 url 信息

添加函数 handleDefaultProtocol 从命令行参数中找到 url 然后处理它。

/**
 * 处理客户端支持的默认协议
 * @param argv
 */
async function handleDefaultProtocol(argv: string[]) {
  const url = defaultProtocolClient.findUrl(argv);
  if (!url) {
    return;
  }
  await dialog.showMessageBox({
    type: "info",
    message: "window protocol 自定义协议打开",
    detail: ` 链接:${url}`,
  });
}

监听 readysecond-instance 事件

参考: 事件: ‘second-instance’

app.addListener("second-instance", async (event, argv) => {
  await handleDefaultProtocol(argv);
});
app.addListener("ready", async () => {
  await createMainWindow();
  await handleDefaultProtocol(process.argv);
});

外部调用

既然我们自定义协议的目的是让外部程序调用,那么如何使用外部调用就很重要了。

首先检查注册表中是否已经包含它了,操作 ctrl+s => 搜索注册表 => 进入注册表 => ctrl+f 查找 custom-protocol

注册表

浏览器打开

如上图所示,可以简单在浏览器中输入 custom-protocol://test 来启动程序。

自定义协议效果

nodejs 示例

在 nodejs 中使用 npm 包 open 可以轻易打开自定义默认链接。

import * as open from "open";

open("custom-protocol://test");

其实本质上就是拼接命令,然后执行系统命令打开 url,参考它的实现