electron入门

菜单

Electron 里的菜单大体上分为三类:

  • 应用菜单:应用菜单通常位于应用程序的顶部,提供了用户可能用到的各种操作,如程序的快捷方式、常用的文件夹及系统命令等。
  • 上下文菜单:在应用里面点击右键看到的菜单。
  • Dock 菜单:只在 OSX 系统才有,通常功能较少,提供特别常用的功能

应用菜单

新建菜单

const menu = new Menu()

新建菜单项

const menuItem = new MenuItem({
    label: '菜单项名',
    click: handler,
    enabled,
    visible,
    type: normal|separator|submenu|checkbox|radio,
    role: copy|paste|cut|quit|...
})

添加菜单项

menu.append(new MenuItem({...}))
menu.append(new MenuItem({type: 'separator'}))
menu.append(new MenuItem({...}))

Electron 的所有内置的 role 如下:

  • undo: 撤销
  • redo:重做
  • cut:剪切
  • copy:复制
  • paste:粘贴
  • pasteAndMatchStyle
  • selectAll:全选
  • delete:删除
  • minimize:当前窗口最小化
  • close:关闭当前窗口
  • quit:退出应用程序
  • reload:刷新当前窗口
  • forceReload:强制刷新当前窗口,忽略缓存
  • toggleDevTools:打开或者关闭 devtool
  • togglefullscreen:进行全屏切换
  • resetZoom:重置窗口大小
  • zoomIn:放大窗口的10%.
  • zoomOut:缩小窗口的10%.

更多请参考: 菜单项 | Electron

新建menu.js

const { app, Menu } = require('electron')

const isMac = process.platform === 'darwin'

const template = [
  // { role: 'appMenu' }
  ...(isMac ? [{
    label: app.name,
    // submenu 代表下一级菜单
    submenu: [
      { role: 'about' },
      { type: 'separator' },
      { role: 'services' },
      { type: 'separator' },
      { role: 'hide' },
      { role: 'hideOthers' },
      { role: 'unhide' },
      { type: 'separator' },
      { role: 'quit' }
    ]
  }] : []),
  // { role: 'fileMenu' }
  {
    label: 'File',
    submenu: [
      isMac ? { role: 'close' } : { role: 'quit' }
    ]
  },
  // { role: 'editMenu' }
  {
    label: 'Edit',
    submenu: [
      { role: 'undo' },
      { role: 'redo' },
      { type: 'separator' },
      { role: 'cut' },
      { role: 'copy' },
      { role: 'paste' },
      ...(isMac ? [
        { role: 'pasteAndMatchStyle' },
        { role: 'delete' },
        { role: 'selectAll' },
        { type: 'separator' },
        {
          label: 'Speech',
          submenu: [
            { role: 'startSpeaking' },
            { role: 'stopSpeaking' }
          ]
        }
      ] : [
        { role: 'delete' },
        { type: 'separator' },
        { role: 'selectAll' }
      ])
    ]
  },
  // { role: 'viewMenu' }
  {
    label: 'View',
    submenu: [
      { role: 'reload' },
      { role: 'forceReload' },
      { role: 'toggleDevTools' },
      { type: 'separator' },
      { role: 'resetZoom' },
      { role: 'zoomIn' },
      { role: 'zoomOut' },
      { type: 'separator' },
      { role: 'togglefullscreen' }
    ]
  },
  // { role: 'windowMenu' }
  {
    label: 'Window',
    submenu: [
      { role: 'minimize' },
      { role: 'zoom' },
      ...(isMac ? [
        { type: 'separator' },
        { role: 'front' },
        { type: 'separator' },
        { role: 'window' }
      ] : [
        { role: 'close' }
      ])
    ]
  },
  {
    role: 'help',
    submenu: [
      {
        label: 'Learn More',
        click: async () => {
          const { shell } = require('electron')
          await shell.openExternal('https://electronjs.org')
        }
      }
    ]
  }
]
// 从模板中创建菜单
const menu = Menu.buildFromTemplate(template)
// 设置为应用程序菜单
Menu.setApplicationMenu(menu)

main.js

const { app, BrowserWindow } = require("electron");

let win;
app.on("ready", () => {
  win = new BrowserWindow({
    width: 600,
    height: 600,
    webPreferences: {
      nodeIntegration: true, // 设置能在页面使用nodejs的API
      contextIsolation: false, // 关闭警告信息
    },
  });
  win.loadFile("./index.html");
  win.webContents.openDevTools();
  require("./menu");
});

 index.html

<html>
    <body>
        <div>菜单</div>
        <ol>
            <li>应用菜单</li>
            <li>上下文菜单</li>
            <li>Dock菜单</li>
        </ol>
    </body>
</html>

 运行npx electron main.js

注意的是:对于 OSX 而言,应用菜单的第一个菜单项是应用程序的名字,会使得 Edit App 这个菜单被覆盖掉。因此,我们需要针对 OSX 进行特殊处理,处理的过程通常是:

if (process.platform === 'darwin') {
    template.unshift({
        label: app.getName(),
        submenu: [
            {
                label: 'Quit',
                accelerator: 'CmdOrCtrl+Q',
                click() {
                    app.quit();
                }
            },
            // ...
        ]
    });
}

上下文菜单

上下文菜单(context menu)就是我们通常说的右键菜单。要创建由渲染器启动的菜单,请通过 IPC 发送所需的信息到主过程,并让主过程代替渲染器显示菜单。

renderer.js

//renderer.js
const { ipcRenderer } = require("electron");

window.addEventListener("contextmenu", (e) => {
  e.preventDefault();
  ipcRenderer.send("show-context-menu");
});

ipcRenderer.on("context-menu-command", (event, command) => {
  // ...
  console.log(event, command);
});

并且在index.html页面中引入将renderer.js 

 修改main.js

// ...
app.on("ready", () => {
  // ...
  handleIPC();
});

function handleIPC() {
  ipcMain.on("show-context-menu", (event) => {
    const contextTemplate = [
      {
        label: "Menu Item 1",
        click: () => {
          event.sender.send("context-menu-command", "menu-item-1");
        },
      },
      {
        label: "Cut",
        role: "cut",
      },
      {
        label: "Copy",
        role: "copy",
      },
    ];
    const menu = Menu.buildFromTemplate(contextTemplate);
    menu.popup(BrowserWindow.fromWebContents(event.sender));
  });
}

运行main.js后,右键: 

Dock菜单

修改main.js

const { app, BrowserWindow, ipcMain, Menu } = require("electron");

let win;
app.on("ready", () => {
  // ...
  createDockMenu();
  
});

const createDockMenu = () => {
  const dockTempalte = [
    {
      label: "New Window",
      click() {
        console.log("New Window");
      },
    },
    {
      label: "New Window with Settings",
      submenu: [{ label: "Basic" }, { label: "Pro" }],
    },
    {
      label: "New Command...",
    },
  ];

  const dockMenu = Menu.buildFromTemplate(dockTempalte);
  app.dock.setMenu(dockMenu);
};

运行后,右键icon

进程间通信

从渲染进程到主进程

Callback写法

  • ipcRenderer.send(channel, ...args)
  • ipcMain.on(channel, handler)

Promise写法

  • ipcRenderer.invoke(channel, ...args)
  • ipcMain.handle(channel, handler)

index.html

<script src="renderer.js"></script>

main.js

const { app, BrowserWindow, ipcMain } = require("electron");

let win;
app.on("ready", () => {
  win = new BrowserWindow({
    width: 300,
    height: 300,
    webPreferences: {
      nodeIntegration: true, //设置能在页面使用nodejs的API
      contextIsolation: false, //关闭警告信息
    },
  });
  win.loadFile("./index.html");
  handleIPC();
});
function handleIPC() {
  ipcMain.on("asynchronous-message", (event, arg) => {
    console.log(arg); // asynchronous ping
    event.reply("asynchronous-reply", "asynchronous pong");
  });

  ipcMain.on("synchronous-message", (event, arg) => {
    console.log(arg); // synchronous ping
    event.returnValue = "synchronous pong";
  });

  ipcMain.handle("my-invokable-ipc", async (event, ...args) => {
    console.log(...args);
  });
}

renderer.js

const { ipcRenderer } = require("electron");
console.log(ipcRenderer.sendSync("synchronous-message", "synchronous ping")); // synchronous pong

ipcRenderer.on("asynchronous-reply", (event, arg) => {
  console.log(arg); // asynchronous pong
});
ipcRenderer.send("asynchronous-message", "asynchronous ping");

ipcRenderer.invoke("my-invokable-ipc", 1, 2);

// 渲染进程
async function hanldeInvoke() {
  const result = await ipcRenderer.invoke("my-invokable-ipc", 1, 2);
  // ...
}

hanldeInvoke();

 运行npx electron main.js

从主进程到渲染进程

主进程通知渲染进程,因为只有一个主进程,可能有多个渲染进程,那么就需要找到具体的窗体内容, 主进程用 window 的 webContent 对象与网页内容进行交互

  • ipcRenderer.on(channel, handler)
  • webContents.send(channel)

main.js

const { app, BrowserWindow } = require("electron");

let win;
app.on("ready", () => {
  win = new BrowserWindow({
    width: 600,
    height: 600,
    webPreferences: {
      nodeIntegration: true, //设置能在页面使用nodejs的API
      contextIsolation: false, //关闭警告信息
    },
  });
  win.loadFile("./index.html");
  handleIPC();
});
function handleIPC() {
  win.webContents.send('do-some-render-work');
}

 renderer.js

const { ipcRenderer } = require("electron");

ipcRenderer.on('do-some-render-work', () => {
    alert('do some work');
})

 运行npx electron main.js

页面间(渲染进程与渲染进程间)通信 

通知事件

  • 利用主进程作为中转站
  • ipcRenderer.sendTo(webContentsId, channel, ...args)

数据共享

  • Web技术(localStorage、sessionStorage、indexedDB)
  • 使用remote(14.0.0版本后被废弃,改成@electron/remote)

利用主进程作消息中转

main.js

const { app, BrowserWindow, ipcMain } = require("electron");

let win, win2;
app.on("ready", () => {
  win = new BrowserWindow({
    width: 600,
    height: 600,
    webPreferences: {
      nodeIntegration: true, //设置能在页面使用nodejs的API
      contextIsolation: false, //关闭警告信息
    },
  });
  win.loadFile("./index.html");

  win2 = new BrowserWindow({
    width: 600,
    height: 600,
    webPreferences: {
      nodeIntegration: true, //设置能在页面使用nodejs的API
      contextIsolation: false, //关闭警告信息
    },
  });
  win2.loadFile("./index2.html");
  handleIPC();
});
function handleIPC() {
  ipcMain.on("message1", (event, arg) => {
    win2.webContents.send("do-some-work", arg);
  });
}

index2.html

<script src="renderer2.js"></script>

renderer.js

const { ipcRenderer } = require("electron");
ipcRenderer.send("message1", "hello!");

renderer2.js

const { ipcRenderer } = require("electron");

ipcRenderer.on('do-some-work', (event, arg) => {
    console.log('renderer2 handle ' + arg);
})

利用ipcRenderer.sendTo

先安装@electron/remote

main.js

const { app, BrowserWindow, ipcMain } = require("electron");
// 初始化
require("@electron/remote/main").initialize();

let win, win2;
app.on("ready", () => {
  win = new BrowserWindow({
    width: 600,
    height: 600,
    webPreferences: {
      nodeIntegration: true, //设置能在页面使用nodejs的API
      contextIsolation: false, //关闭警告信息
    },
  });
  win.loadFile("./index.html");
  require("@electron/remote/main").enable(win.webContents);

  win2 = new BrowserWindow({
    width: 600,
    height: 600,
    webPreferences: {
      nodeIntegration: true, //设置能在页面使用nodejs的API
      contextIsolation: false, //关闭警告信息
    },
  });
  win2.loadFile("./index2.html");

  global.sharedObject = {
    win2WebContentsId: win2.webContents.id,
  };

});

 renderer.js

const { ipcRenderer } = require("electron");
const remote = require('@electron/remote');

// 利用remote 、sendTo
let sharedObject = remote.getGlobal("sharedObject");
let win2WebContentsId = sharedObject.win2WebContentsId;
ipcRenderer.sendTo(win2WebContentsId, "do-some-work", "hello!");

renderer2.js

const { ipcRenderer } = require("electron");

ipcRenderer.on("do-some-work", (event, arg) => {
  console.log("renderer2 handle " + arg);
});

利用本地存储进行通信

renderer.js

window.onload = function () {
  localStorage.setItem("message", "hello!");
};

 renderer2.js

window.onload = function () {
  var msg = localStorage.getItem("message");
  alert(msg);
};

参考资料:

ipcMain | Electron

https://segmentfault.com/a/1190000020521879

Electron开发实战-极客时间

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值