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(...) 里面、建窗口之前执行。
💡 动手改
- 给
filters加上json扩展名,再建一个test.json验证能被选中 - 把「关于」菜单项换成自定义行为:
click: () => dialog.showMessageBox({ message: '青蛙出品' }) - 结合
app.getPath('documents'):在界面显示用户的文档目录路径
📦 本章小结
- 原生模块全部住在主进程,界面通过 IPC 频道使用它们
dialog返回canceled要先判断,取消就返回nullNotification一行弹出系统通知;Menu+role快速搭出标准菜单- 常用组合拳:dialog 选路径 → Node 读写文件 → Notification 报告结果(第 8 章记事本就用它)
下一章:打包与发布 🐸