首页
/ Electron ShareMenu 详解:在 macOS 桌面应用中调用系统分享菜单(Share Menu)

Electron ShareMenu 详解:在 macOS 桌面应用中调用系统分享菜单(Share Menu)

2026-09-06 11:55:07作者:昌雅子Ethen

Electron 的 ShareMenu 类让 macOS 应用能够直接唤起系统级的分享菜单(Share Menu),把文本、文件或 URL 从当前上下文分享到 App、社交账号和其他服务。本文基于 Electron 仓库中的 API 文档 docs/api/share-menu.md、配套的 SharingItem 结构 以及主进程 JS/C++ 源码,完整讲解其用法、参数默认值、底层实现链路,以及如何把它以菜单子项(shareMenu role)的方式嵌入应用菜单。

什么是 ShareMenu

ShareMenu 是一个主进程(Main process)类,用于在 macOS 上创建系统分享菜单。这个概念对应 Apple 的 Share Extensions(HIG 中的 "Share Menu")机制:用户点击分享后,由系统枚举本机可用的分享目标(信息、备忘录、AirDrop、各社交 App 等),开发者不需要自己实现任何分享渠道。

docs/api/share-menu.md 的 API history 注释看,该 API 由上游 PR electron/electron#25629 引入。文档同时给出两条使用路径:

  1. 独立类new ShareMenu(sharingItem),然后 popup() 以右键上下文菜单的形式弹出——适合"在页面某处点一下直接分享"的交互;
  2. 菜单 role:作为其他菜单的子菜单时,改用 MenuItemshareMenu role(见下文第五节)。

文档还沿用了 Electron 内置类的通用约束,值得原样保留:

警告:Electron 的内置类不能在用户代码中被继承(subclassed),详见 FAQ

分享内容的载体:SharingItem 结构

ShareMenu 构造函数唯一参数是一个 SharingItem 对象,它描述"要分享什么"。按 docs/api/structures/sharing-item.md 的定义,它只有三个可选字段,且都是数组:

字段 类型 说明
texts string[] (optional) 要分享的文本数组
filePaths string[] (optional) 要分享的文件路径数组
urls string[] (optional) 要分享的 URL 数组

三者可以任意组合,例如"一段文字 + 一个链接 + 两个图片文件":

const sharingItem = {
  texts: ['这是我在 My Electron App 中选中的内容'],
  urls: ['https://example.com/article/42'],
  filePaths: ['/tmp/screenshot-1.png', '/tmp/screenshot-2.png']
};

这个结构不只是 ShareMenu 专用——它同样被 MenuItemsharingItem 属性复用(当 roleshareMenu 时生效),两种入口共享同一份数据模型。

创建与弹出:new ShareMenu()popup()

new ShareMenu(sharingItem)

const { ShareMenu } = require('electron');

const sharingItem = { texts: ['Hello from Electron'] };
const shareMenu = new ShareMenu(sharingItem);

从源码看,JS 层的 ShareMenu 是一个非常薄的包装,见 lib/browser/api/share-menu.ts

class ShareMenu implements Electron.ShareMenu {
  private menu: Menu;

  constructor(sharingItem: SharingItem) {
    this.menu = new (Menu as any)({ sharingItem });
  }

  popup(options?: PopupOptions) {
    this.menu.popup(options);
  }

  closePopup(browserWindow?: BrowserWindow) {
    this.menu.closePopup(browserWindow);
  }
}

也就是说 ShareMenu 本质上是一个携带了 sharingItem 构造参数的 Menu,其弹出与关闭逻辑完全复用了 Menu 的既有实现。

shareMenu.popup([options])

把分享菜单作为上下文菜单弹出到 BrowserWindow 中。PopupOptions 的参数及默认值如下(完整继承自 docs/api/share-menu.md):

参数 类型 默认值 说明
browserWindow BrowserWindow (optional) 当前聚焦窗口 指定弹出所在的窗口
x number (optional) 当前鼠标光标位置 若声明了 y 则必须同时声明
y number (optional) 当前鼠标光标位置 若声明了 x 则必须同时声明
positioningItem number (optional, macOS) -1 指定哪个菜单项索引被定位到鼠标光标下方
callback Function (optional) 菜单关闭时被调用

这些默认值不是文档的"口头承诺",在 lib/browser/api/menu.tsMenu.prototype.popup 实现中可以逐一验证:

  • xy 未传时默认置为 -1,由原生层解释为"跟随鼠标光标"(第 114-115 行);
  • positioningItem 未传时默认为 -1(第 116 行);
  • browserWindow 的解析有一条回退链:先校验传入窗口是否存在于 BaseWindow.getAllWindows() 中,不存在则取 getFocusedWindow();仍没有则取窗口列表第一个;一个窗口都没有时直接抛出 Cannot open Menu without a BaseWindow present(第 120-129 行)。

典型用法(例如在渲染进程右键后由主进程弹出):

// 在主进程中
const shareMenu = new ShareMenu({
  urls: [currentArticleUrl],
  texts: [currentTitle]
});

// 在指定坐标弹出
shareMenu.popup({
  browserWindow: mainWindow,
  x: 100,
  y: 200,
  callback: () => console.log('分享菜单已关闭')
});

// 或者:跟随鼠标位置弹出(不传 x/y)
shareMenu.popup();

注意 x/y 的成对约束:声明其中一个就必须同时声明另一个,否则坐标语义不完整,应按文档约定成对传入。

shareMenu.closePopup([browserWindow])

关闭在 browserWindow(默认聚焦窗口)中弹出的该分享菜单:

shareMenu.closePopup(mainWindow); // 或 shareMenu.closePopup() 关闭聚焦窗口中的菜单

底层实现见 lib/browser/api/menu.ts:传入的窗口必须是 BaseWindow 实例,此时按窗口 ID 精确关闭对应 runner;若不传(或不是 BaseWindow),则以 -1 作为参数,表示关闭属于该菜单的所有 runner。

源码实现链路:从 JS 选项到原生 NSMenu

从源码结构看,SharingItem 的流转路径横跨 JS 与 C++ 两层,且整条链路被 macOS 编译条件严格限定:

  1. JS 层lib/browser/api/share-menu.tssharingItem 塞进 Menu 的构造参数对象中;
  2. C++ 构造解析:在 shell/browser/api/electron_api_menu.cc 中,Menu::Menu 的构造函数只在 #if BUILDFLAG(IS_MAC) 分支内读取 options.Get("sharingItem", &item),然后调用 model_->SetSharingItem(std::move(item)) 写入 ElectronMenuModel。这从编译层面确认了 ShareMenu 仅支持 macOS——非 macOS 平台上传入的 sharingItem 会被静默忽略;
  3. 菜单模型回传 JSlib/browser/api/menu.ts 中,Menu.prototype._getSharingItemForCommandId 这一回调方法同样被 process.platform === 'darwin' 包裹,即原生菜单在构建某个带分享能力的项时,会通过 commandId 反查 JS 侧缓存的 sharingItem
  4. 原生 UI 构建:最终消费发生在 shell/browser/ui/cocoa/electron_menu_controller.mm(Cocoa 平台实现)。其中 ConvertSharingItemToNS 负责把 SharingItem 转换为 NSObjects 数组(对应系统分享框架所需的 NSItemProvider 数据源),createShareMenuForItem: 再用该数组创建真正的原生分享菜单。

这条链路解释了前文的两个行为特征:为什么 ShareMenu 只是 Menu 的薄包装,以及为什么平台差异被固化在编译分支而非运行时判断中。

另一种用法:作为菜单子项(shareMenu role)

docs/api/share-menu.md 明确指出:想把分享菜单作为其他菜单的子菜单时,不应使用 ShareMenu 类,而应使用 MenuItemshareMenu role。

docs/api/menu-item.md 中,role 的可选值包含 shareMenu,并配套一个专用属性:

  • sharingItem SharingItem (optional) macOS —— "当 roleshareMenu 时要分享的内容"(见 docs/api/menu-item.md 第 48、183 行的字段定义);
  • docs/tutorial/menus.md 的 role 列表中同样说明:shareMenu 对应的子菜单就是 share menu,且必须同时设置 sharingItem 属性指明分享对象。

示例——把分享项挂到"文件"菜单下:

const { Menu } = require('electron');

const template = [
  {
    label: '文件',
    submenu: [
      {
        label: '分享',
        role: 'shareMenu',
        sharingItem: {
          texts: ['来自 Electron 的分享内容'],
          urls: ['https://example.com']
        }
      },
      { type: 'separator' },
      { role: 'quit' }
    ]
  }
];

Menu.setApplicationMenu(Menu.buildFromTemplate(template));

两种用法的选型原则:需要跟随鼠标弹出、交互即开即关的场景用 ShareMenu 类 + popup();需要常驻在应用菜单/右键菜单结构中时,用 shareMenu role 声明为子菜单

使用注意事项

  1. 平台限制ShareMenusharingItem 均为 macOS 专属能力。C++ 侧的解析代码位于 BUILDFLAG(IS_MAC) 编译分支(shell/browser/api/electron_api_menu.cc),在 Windows/Linux 上该 API 不会产生系统分享菜单,跨平台应用应自行做 process.platform === 'darwin' 判断并提供降级交互;
  2. 不可继承:与所有 Electron 内置类一样,ShareMenu 不能被用户代码 extends(见 docs/faq.md 的类继承说明);
  3. 坐标成对约束popup()xy 必须同时声明或同时省略,省略时回落到鼠标光标位置(lib/browser/api/menu.ts 中默认值 -1 即此语义);
  4. 回调时机popup()callback 在菜单关闭时触发(而非弹出时),适合用于清理状态或恢复焦点;
  5. closePopup() 的作用域:不传窗口参数时会关闭该菜单实例对应的所有 runner(lib/browser/api/menu.ts),多窗口同时弹出同一 ShareMenu 时需注意这一点;
  6. positioningItem 用于多行菜单定位:指定菜单打开后哪一行(索引)停在鼠标下方,默认 -1 表示不做特殊定位。

参考文件

登录后查看全文
热门项目推荐
相关项目推荐