从其他应用中的 URL 启动您的应用
#
概览This guide will take you through the process of setting your Electron app as the default handler for a specific protocol.
通过此教程,您会掌握如何设置您的应用以拦截并处理任意特定协议的URL的点击事件。 在本指南中,我们假定这个协议名为“electron-fiddle://
”。
#
示例#
主进程(main.js)First, we will import the required modules from electron
. These modules help control our application lifecycle and create a native browser window.
const { app, BrowserWindow, shell } = require('electron')const path = require('path')
其次,我们将应用注册为“electron-fiddle://
”协议的处理器。
if (process.defaultApp) { if (process.argv.length >= 2) { app.setAsDefaultProtocolClient('electron-fiddle', process.execPath, [path.resolve(process.argv[1])]) }} else { app.setAsDefaultProtocolClient('electron-fiddle')}
现在我们定义负责创建浏览器窗口的函数,并加载应用的 index.html
文件。
const createWindow = () => { // Create the browser window. mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: path.join(__dirname, 'preload.js') } })
mainWindow.loadFile('index.html')}
紧接着,我们将创建 BrowserWindow
并在应用中定义如何处理此外部协议被点击的事件。
This code will be different in Windows compared to MacOS and Linux. This is due to Windows requiring additional code in order to open the contents of the protocol link within the same Electron instance. 请点击 此处 了解更多
#
Windows 下代码:const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) { app.quit()} else { app.on('second-instance', (event, commandLine, workingDirectory) => { // 用户正在尝试运行第二个实例,我们需要让焦点指向我们的窗口 if (mainWindow) { if (mainWindow.isMinimized()) mainWindow.restore() mainWindow.focus() } })
// 创建 mainWindow,加载其他资源,等等…… app.whenReady().then(() => { createWindow() })
// Handle the protocol. 在本例中,我们选择显示一个错误提示对话框。 app.on('open-url', (event, url) => { dialog.showErrorBox('欢迎回来', `导向自: ${url}`) })}
#
MacOS 与 Linux 下代码:// Electron 在完成初始化,并准备创建浏览器窗口时,// 会调用这个方法。// 部分 API 在 ready 事件触发后才能使用。app.whenReady().then(() => { createWindow()})
// Handle the protocol. 在本例中,我们选择显示一个错误提示对话框。app.on('open-url', (event, url) => { dialog.showErrorBox('欢迎回来', `导向自: ${url}`)})
Finally, we will add some additional code to handle when someone closes our application.
// 在除 MacOS 的其他平台上,当所有窗口关闭后,退出当前应用。 There, it's common// for applications and their menu bar to stay active until the user quits// explicitly with Cmd + Q.app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit()})
#
Important notes#
打包On macOS and Linux, this feature will only work when your app is packaged. It will not work when you're launching it in development from the command-line. When you package your app you'll need to make sure the macOS Info.plist
and the Linux .desktop
files for the app are updated to include the new protocol handler. Some of the Electron tools for bundling and distributing apps handle this for you.
Electron Forge#
If you're using Electron Forge, adjust packagerConfig
for macOS support, and the configuration for the appropriate Linux makers for Linux support, in your Forge configuration (please note the following example only shows the bare minimum needed to add the configuration changes):
{ "config": { "forge": { "packagerConfig": { "protocols": [ { "name": "Electron Fiddle", "schemes": ["electron-fiddle"] } ] }, "makers": [ { "name": "@electron-forge/maker-deb", "config": { "mimeType": ["x-scheme-handler/electron-fiddle"] } } ] } }}
Electron Packager#
For macOS support:
If you're using Electron Packager's API, adding support for protocol handlers is similar to how Electron Forge is handled, except protocols
is part of the Packager options passed to the packager
function.
const packager = require('electron-packager')
packager({ // ...other options... protocols: [ { name: 'Electron Fiddle', schemes: ['electron-fiddle'] } ]
}).then(paths => console.log(`SUCCESS: Created ${paths.join(', ')}`)) .catch(err => console.error(`ERROR: ${err.message}`))
If you're using Electron Packager's CLI, use the --protocol
and --protocol-name
flags. For example:
npx electron-packager . --protocol=electron-fiddle --protocol-name="Electron Fiddle"
#
结论After you start your Electron app, you can enter in a URL in your browser that contains the custom protocol, for example "electron-fiddle://open"
and observe that the application will respond and show an error dialog box.
- index.html
- main.js
- preload.js
- renderer.js
<!DOCTYPE html><html>
<head> <meta charset="UTF-8"> <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP --> <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'"> <meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'"> <title>app.setAsDefaultProtocol Demo</title></head>
<body> <h1>App Default Protocol Demo</h1>
<p>The protocol API allows us to register a custom protocol and intercept existing protocol requests.</p> <p>These methods allow you to set and unset the protocols your app should be the default app for. Similar to when a browser asks to be your default for viewing web pages.</p>
<p>Open the <a href="https://www.electronjs.org/docs/api/protocol">full protocol API documentation</a> in your browser.</p>
-----
<h3>Demo</h3> <p> First: Launch current page in browser <button id="open-in-browser" class="js-container-target demo-toggle-button"> Click to Launch Browser </button> </p>
<p> Then: Launch the app from a web link! <a href="electron-fiddle://open">Click here to launch the app</a> </p>
----
<p>You can set your app as the default app to open for a specific protocol. For instance, in this demo we set this app as the default for <code>electron-fiddle://</code>. The demo button above will launch a page in your default browser with a link. Click that link and it will re-launch this app.</p>
<h3>Packaging</h3> <p>This feature will only work on macOS when your app is packaged. It will not work when you're launching it in development from the command-line. When you package your app you'll need to make sure the macOS <code>plist</code> for the app is updated to include the new protocol handler. If you're using <code>electron-packager</code> then you can add the flag <code>--extend-info</code> with a path to the <code>plist</code> you've created. The one for this app is below:</p>
<p> <h5>macOS plist</h5> <pre><code> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>CFBundleURLTypes</key> <array> <dict> <key>CFBundleURLSchemes</key> <array> <string>electron-api-demos</string> </array> <key>CFBundleURLName</key> <string>Electron API Demos Protocol</string> </dict> </array> <key>ElectronTeamID</key> <string>VEKTX9H2N7</string> </dict> </plist> </code> </pre> <p>
<!-- You can also require other files to run in this process --> <script src="./renderer.js"></script></body>
</html>
// Modules to control application life and create native browser windowconst { app, BrowserWindow, ipcMain, shell, dialog } = require('electron')const path = require('path')
let mainWindow;
if (process.defaultApp) { if (process.argv.length >= 2) { app.setAsDefaultProtocolClient('electron-fiddle', process.execPath, [path.resolve(process.argv[1])]) }} else { app.setAsDefaultProtocolClient('electron-fiddle')}
const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) { app.quit()} else { app.on('second-instance', (event, commandLine, workingDirectory) => { // Someone tried to run a second instance, we should focus our window. if (mainWindow) { if (mainWindow.isMinimized()) mainWindow.restore() mainWindow.focus() } })
// Create mainWindow, load the rest of the app, etc... app.whenReady().then(() => { createWindow() }) app.on('open-url', (event, url) => { dialog.showErrorBox('Welcome Back', `You arrived from: ${url}`) })}
function createWindow () { // Create the browser window. mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: path.join(__dirname, 'preload.js'), } })
mainWindow.loadFile('index.html')}
// Quit when all windows are closed, except on macOS. There, it's common// for applications and their menu bar to stay active until the user quits// explicitly with Cmd + Q.app.on('window-all-closed', function () { if (process.platform !== 'darwin') app.quit()})
// Handle window controls via IPCipcMain.on('shell:open', () => { const pageDirectory = __dirname.replace('app.asar', 'app.asar.unpacked') const pagePath = path.join('file://', pageDirectory, 'index.html') shell.openExternal(pagePath)})
// All of the Node.js APIs are available in the preload process.// It has the same sandbox as a Chrome extension.const { contextBridge, ipcRenderer } = require('electron')
// Set up context bridge between the renderer process and the main processcontextBridge.exposeInMainWorld( 'shell', { open: () => ipcRenderer.send('shell:open'), })
// This file is required by the index.html file and will// be executed in the renderer process for that window.// All APIs exposed by the context bridge are available here.
// Binds the buttons to the context bridge API.document.getElementById('open-in-browser').addEventListener('click', () => { shell.open();});