🐸 青蛙编程网
首页 / 前端 / Electron / 6. 原生能力

6. 原生能力

这是什么:原生能力 = Electron 内置的系统能力模块:文件对话框 dialog、应用菜单 Menu、系统通知 Notification 等。

为什么用:这些是"桌面软件"和"网页"的分水岭——网页弹不出系统文件选择框、改不了应用菜单,桌面软件可以,而且都是现成 API,一行调用。

如何用:套路和第 5 章完全一样——主进程干活,IPC 暴露给界面。本章把最常用的三件套各接一个。

6.1 文件对话框 dialog

main.js 里加一个"选文件"频道:

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

function createWindow() {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: { preload: path.join(__dirname, 'preload.js') },
  })
  win.loadFile('index.html')
}

app.whenReady().then(() => {
  ipcMain.handle('pick-file', async () => {
    const result = await dialog.showOpenDialog({
      title: '选择一个文件',
      properties: ['openFile'],
      filters: [{ name: '文本文件', extensions: ['txt', 'md', 'js'] }],
    })
    if (result.canceled) return null
    return result.filePaths[0]
  })
  createWindow()
})

app.on('window-all-closed', () => {
  app.quit()
})
代码 作用
properties: ['openFile'] 单选文件模式(多选用 ['openFile', 'multiSelections'])
filters 对话框里只显示指定扩展名的文件
result.canceled 用户点了取消——必须处理,返回 null 让界面知道没选
result.filePaths[0] 选中文件的绝对路径,比如 E:\notes\hello.txt

preload.js 加一行:

const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('electronAPI', {
  pickFile: () => ipcRenderer.invoke('pick-file'),
})

renderer.js 接上:

document.getElementById('btn-pick').addEventListener('click', async () => {
  const filePath = await window.electronAPI.pickFile()
  document.getElementById('out').textContent =
    filePath ? '你选了:' + filePath : '已取消'
})

你会看到:点按钮弹出操作系统原生的文件选择框,选中后界面显示完整路径。

6.2 系统通知 Notification

主进程加频道:

ipcMain.handle('notify', (_event, message) => {
  new Notification({
    title: '来自我的应用',
    body: message,
  }).show()
})

preload 暴露 notify: (msg) => ipcRenderer.invoke('notify', msg),界面调用:

await window.electronAPI.notify('任务完成!')

你会看到:屏幕右下角(Windows)弹出系统级通知气泡——和微信新消息同一套样式。第一次运行 Windows 可能询问"是否允许通知",选允许即可。

6.3 应用菜单 Menu

菜单不用 IPC,主进程直接设置(放 app.whenReady() 里):

const { Menu } = require('electron')

const menu = Menu.buildFromTemplate([
  {
    label: '文件',
    submenu: [
      { role: 'reload', label: '重新加载' },
      { type: 'separator' },
      { role: 'quit', label: '退出' },
    ],
  },
  {
    label: '帮助',
    submenu: [
      { role: 'about', label: '关于' },
    ],
  },
])
Menu.setApplicationMenu(menu)
写法 含义
role: 'quit' 官方预置行为(退出/复制/粘贴等),自带快捷键和本地化
label 菜单显示文字,role 的默认英文标签可以用它覆盖成中文
type: 'separator' 一条分隔线

你会看到:窗口顶部菜单栏出现「文件」「帮助」,各项功能直接可用。

6.4 能力速查

模块 干什么 何时用
dialog 打开/保存文件对话框、消息框 一切要用户选文件的场景
Notification 系统通知 任务完成、到点提醒
Menu 应用菜单、右键菜单 软件标配顶栏
app 应用信息与路径,如 app.getPath('documents') 找用户的"文档"目录存东西
clipboard 读写剪贴板 复制粘贴功能

❓ 常见翻车现场

Q1:dialog.showOpenDialog is not a function

原因:dialog 只在主进程可用,写进 renderer.js 或 preload.js 就会报错。 解决:原生模块一律 require 在 main.js,界面侧走 IPC 调用。

Q2:Windows 上通知弹不出来

原因:系统设置里关了通知,或首次运行没授权。 解决:Windows 设置 → 系统 → 通知,允许该应用;开发期重启一次应用再试。

Q3:菜单写了但窗口顶部没有

原因:Menu.setApplicationMenu(menu) 忘了调用,或放在了 whenReady 之外执行过早。 解决:确认这行在 app.whenReady().then(...) 里面、建窗口之前执行。

💡 动手改

  1. 给 filters 加上 json 扩展名,再建一个 test.json 验证能被选中
  2. 把「关于」菜单项换成自定义行为:click: () => dialog.showMessageBox({ message: '青蛙出品' })
  3. 结合 app.getPath('documents'):在界面显示用户的文档目录路径

📦 本章小结

  • 原生模块全部住在主进程,界面通过 IPC 频道使用它们
  • dialog 返回 canceled 要先判断,取消就返回 null
  • Notification 一行弹出系统通知;Menu + role 快速搭出标准菜单
  • 常用组合拳:dialog 选路径 → Node 读写文件 → Notification 报告结果(第 8 章记事本就用它)

下一章:打包与发布 🐸

📥 下载本章代码

ZIP 内含本章全部代码文件与说明,解压即用,与教程内容同步生成。