桌面应用的菜单是我们很常用的一个功能,大到各种应用,小到一个小小的记事本,都有自己的菜单,那么接下来就看看如何实现一个菜单
Electron提供了一个模块Menu来实现菜单功能,我们直接通过一个demo来演示
首先创建如图的目录
这里menu文件夹下的menu.js是用来写菜单的信息,包括菜单的内容,以及菜单的点击事件触发的回调,这里实际上是在主进程中调用的,但是为了可以区分功能和复用,我写在menu.js中,并在主进程中require它
主进程index.js
const { app, BrowserWindow } = require('electron')
// app:管理Electron应用程序的声明周期
// BrowserWindow负责创建窗口
let mainWindow = null // 要打开的主窗口应用
app.on('ready', () => { // 监听准备事件
mainWindow = new BrowserWindow({ // 传入对象设置窗口参数,这里设置了宽和高以及允许使用node的API
width: 500,
height: 500,
webPreferences: {
nodeIntegration: true
}
})
require('./menu/menu.js')
mainWindow.loadFile('index.html')
mainWindow.on('closed', () => { // 监听关闭时间
mainWindow = null // 将窗口置为null,否则的话内存会一直被占用
})
})
这里menu.js也不做过多解释了,其实看代码也已经很清晰了,唯一要说的是setApplicationMenu方法,它在macOS环境和Windows/Linux环境中的表现不同,在macOS中,会把传入的菜单menu设置为应用程序菜单,而在Windows/Linux中,menu会被设置为每个应用的顶部菜单。
menu.js
const { Menu, BrowserWindow } = require('electron')
let template = [{
label: '文件',
submenu: [{
label: '新建窗口',
click: () => { // 添加点击对应的操作,这里为打开新的窗口
let newWin = new BrowserWindow({
width: 500,
height: 500,
webPreferences: {
nodeIntegration: true
}
})
newWin.loadFile('index2.html')
newWin.on('closed', () => {
newWin = null
})
}
},
{ label: '关闭窗口' }
]
}, {
label: '编辑',
submenu: [
{ label: '撤销' },
{ label: '恢复' }
]
}]
const menu = Menu.buildFromTemplate(template) // 根据template生成一个Menu对象
Menu.setApplicationMenu(menu) // 根据menu生成菜单
除了使用模板的形式外,我们还可以通过往Menu对象中添加MenuItem来添加新的菜单项,因为Menu.buildFromTemplate会返回一个Menu对象,所以这里我们在menu.js中添加
const menu = Menu.buildFromTemplate(template) // 根据template生成一个Menu对象
menu.append(new MenuItem({label:'click',click(){console.log('click')}})) // 在menu中添加新的菜单项
Menu.setApplicationMenu(menu) // 根据menu生成菜单
然后运行该应用,点击click,发现控制台打印出了click
这种做法的好处在于,我们可以动态地修改我们的菜单项,这样根据使用应用的人权限不同,我们可以提供不同的菜单项比如下面这个小demo,我在global.json中添加了identity属性,然后在menu.js中通过读取该配置文件identity属性对应的值,提供不同的菜单项,代码如下
我在根目录下添加了一个VIP.html文件,在点击新创建的菜单项时打开这个页面
global.json
{
"name":"前端小白",
"color":"black",
"identity":"VIP"
}
VIP.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>VIPtitle>
head>
<body>
<p>欢迎金主爸爸p>
body>
html>
menu.js
const { Menu, BrowserWindow, MenuItem, Notification} = require('electron')
const fs = require('fs')
let template = [{
label: '文件',
submenu: [{
label: '新建窗口',
click: () => {
let newWin = new BrowserWindow({
width: 500,
height: 500,
webPreferences: {
nodeIntegration: true
}
})
newWin.loadFile('index2.html')
newWin.on('closed', () => {
newWin = null
})
}
},
{ label: '新建文件' }
]
}, {
label: '编辑',
submenu: [
{ label: '撤销' },
{ label: '恢复' }
]
}]
const menu = Menu.buildFromTemplate(template) // 根据template生成一个Menu对象
const { identity } = JSON.parse(fs.readFileSync('global.json'))
if(identity === "VIP"){
menu.append(new MenuItem({label:'会员点击',click(){
let newWin = new BrowserWindow({
width: 500,
height: 500,
webPreferences: {
nodeIntegration: true
}
})
newWin.loadFile('VIP.html')
newWin.on('closed', () => {
newWin = null
})
}}))
}
Menu.setApplicationMenu(menu) // 根据menu生成菜单
点击之后我们可以看到,打开了一个新的页面
金主爸爸的好感度直线上升,又能继续给我们氪金了
而如果global.json中identity的值不为VIP,那么就不会显示这个菜单项
{
"name":"前端小白",
"color":"black",
"identity":"非VIP"
}
实际上上面已经用到了好几个MenuItem的属性,即使是通过使用传入模板然后使用buildFromTemplate的方法,模板中的对象也和传入MenuItem的参数一致,这里说说MenuItem的几个常见的属性
右键菜单的编辑,其实和顶部菜单的编辑差不多,只是调用了Menu的另一个API:popup
首先,我们看一下这个API
这里的默认打开窗口,也可以通过romate拿到主线程的getCurrentWindow方法来得到当前窗口对应的BrowserWindow对象
const { remote } = require('electron')
const { BrowserWindow, Menu, MenuItem } = remote
window.addEventListener('load',()=>{
const menu = new Menu() // 创建一个Menu对象
menu.append(new MenuItem({ label: 'MenuItem1', click() { console.log('item 1 clicked') } })) // 添加MenuItem菜单项
window.addEventListener('contextmenu', (e) => {
e.preventDefault()
menu.popup({ window: remote.getCurrentWindow() })
}, false)
})
这里要注意的是,我们需要打开控制台才能看到打印的内容,但是这里因为我们使用了自己的顶部菜单,所以也无法使用ctrl+shift+i来打开控制台,这里需要把控制台添加到我们的菜单中,也可以直接添加在右键点击菜单里
const menu = new Menu() // 创建一个Menu对象
menu.append(new MenuItem({ label: 'MenuItem1', click() { console.log('item 1 clicked') } })) // 添加MenuItem菜单项
menu.append(new MenuItem({ label: '打开开发者工具', role: 'toggledevtools' }))
window.addEventListener('contextmenu', (e) => {
e.preventDefault()
menu.popup({ window: remote.getCurrentWindow() })
}, false)