Electron 深度鏈接(Deep Links)

2023-02-16 17:15 更新

概覽?

本指南將會指導(dǎo)你配置 Electron 應(yīng)用為 特定協(xié)議 的默認(rèn)處理器。

通過此教程,您會掌握如何設(shè)置您的應(yīng)用以攔截并處理任意特定協(xié)議的URL的點擊事件。 在本指南中,我們假定這個協(xié)議名為“electron-fiddle://”。

示例?

主進程(main.js)

首先,我們需要從electron導(dǎo)入所需的模塊。 這些模塊有助于控制應(yīng)用的生命周期,或創(chuàng)建原生的瀏覽器窗口。

const { app, BrowserWindow, shell } = require('electron')
const path = require('path')

其次,我們將應(yīng)用注冊為“electron-fiddle://”協(xié)議的處理器。

if (process.defaultApp) {
  if (process.argv.length >= 2) {
    app.setAsDefaultProtocolClient('electron-fiddle', process.execPath, [path.resolve(process.argv[1])])
  }
} else {
  app.setAsDefaultProtocolClient('electron-fiddle')
}

現(xiàn)在我們定義負(fù)責(zé)創(chuàng)建瀏覽器窗口的函數(shù),并加載應(yīng)用的 index.html 文件。

const createWindow = () => {
  // 創(chuàng)建瀏覽器窗口
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  })

  mainWindow.loadFile('index.html')
}

緊接著,我們將創(chuàng)建 BrowserWindow 并在應(yīng)用中定義如何處理此外部協(xié)議被點擊的事件。

與 MacOS 或 Linux 不同,在 Windows 下需要其他的代碼。 這是因為在 Windows 中需要特別處理在同一個 Electron 實例中打開的協(xié)議的內(nèi)容。

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()
    }
  })

  // 創(chuàng)建 mainWindow,加載其他資源,等等……
  app.whenReady().then(() => {
    createWindow()
  })

  // 處理協(xié)議。 在本例中,我們選擇顯示一個錯誤提示對話框。
  app.on('open-url', (event, url) => {
    dialog.showErrorBox('歡迎回來', `導(dǎo)向自: ${url}`)
  })
}

MacOS 與 Linux 下代碼:

// Electron 在完成初始化,并準(zhǔn)備創(chuàng)建瀏覽器窗口時,
// 會調(diào)用這個方法。
// 部分 API 在 ready 事件觸發(fā)后才能使用。
app.whenReady().then(() => {
  createWindow()
})

// 處理協(xié)議 在本例中,我們選擇顯示一個錯誤提示對話框。
app.on('open-url', (event, url) => {
  dialog.showErrorBox('歡迎回來', `導(dǎo)向自: ${url}`)
})

最后,我們還需要處理應(yīng)用的關(guān)閉事件。

// 在除 MacOS 的其他平臺上,當(dāng)所有窗口關(guān)閉后,退出當(dāng)前應(yīng)用。 在 MacOS 上,
// 應(yīng)用及其菜單欄通常會保持活躍狀態(tài),
// 直到用戶明確按下 Cmd + Q 退出應(yīng)用
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit()
})

重要提醒:?

打包

在 macOS 和 Linux 上,此功能僅在應(yīng)用打包后才有效。 在命令行啟動的開發(fā)版中無效。 當(dāng)您打包應(yīng)用程序時,你需要確保應(yīng)用程序的 macOS Info.plist 和 Linux .desktop 文件已更新以包含新的協(xié)議處理程序。 一些綁定和分發(fā)應(yīng)用程序的 Electron 工具會自動為你處理

Electron Forge

如果您使用 Electron Forge,請調(diào)整 macOS 支持的 packagerConfig ,以及適當(dāng)調(diào)整 Linux 制造商的 Linux 支持配置,在 Forge 配置 (請注意以下示例僅顯示添加配置時所需更改的最低值) 

{
  "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?

對于 macOS 支持:

如果您正在使用 Electron Packager 的 API,添加對協(xié)議處理程序的支持類似于 Electron Forge 的處理方式, 其他 protocols 是傳遞到 packager 函數(shù)的 Packager 選項的一部分。

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}`))

如果您使用 Electron Packager 的 CLI,請使用 --protocol 和 --protocol-name 標(biāo)志。 例如:

npx electron-packager . --protocol=electron-fiddle --protocol-name="Electron Fiddle"

結(jié)論

啟動 Electron 應(yīng)用后,在瀏覽器內(nèi)鍵入包含該自定義協(xié)議的 URL,如 "electron-fiddle://open" ,觀察應(yīng)用是否正確響應(yīng)并顯示一個錯誤提示對話框。

 main.js preload.js  index.html  renderer.js 
// Modules to control application life and create native browser window
const { 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 IPC
ipcMain.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 process
contextBridge.exposeInMainWorld(
  'shell',
  {
    open: () => ipcRenderer.send('shell:open'),
  }
)
<!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  rel="external nofollow" target="_blank" >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" rel="external nofollow" target="_blank" >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>
    &lt;?xml version="1.0" encoding="UTF-8"?&gt;
    &lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;
            &lt;plist version="1.0"&gt;
                &lt;dict&gt;
                    &lt;key&gt;CFBundleURLTypes&lt;/key&gt;
                    &lt;array&gt;
                        &lt;dict&gt;
                            &lt;key&gt;CFBundleURLSchemes&lt;/key&gt;
                            &lt;array&gt;
                                &lt;string&gt;electron-api-demos&lt;/string&gt;
                            &lt;/array&gt;
                            &lt;key&gt;CFBundleURLName&lt;/key&gt;
                            &lt;string&gt;Electron API Demos Protocol&lt;/string&gt;
                        &lt;/dict&gt;
                    &lt;/array&gt;
                    &lt;key&gt;ElectronTeamID&lt;/key&gt;
                    &lt;string&gt;VEKTX9H2N7&lt;/string&gt;
                &lt;/dict&gt;
            &lt;/plist&gt;
        </code>
    </pre>
  <p>

    <!-- You can also require other files to run in this process -->
    <script src="./renderer.js"></script>
</body>

</html>
// 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();
});

docs/fiddles/system/protocol-handler/launch-app-from-URL-in-another-app

Open in Fiddle


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號