likes
comments
collection
share

Electron食用指南: 简单介绍主线程、渲染线程, 以及常用的通讯方法

作者站长头像
站长
· 阅读数 82

Electron是一个使用JavaScript、HTML和CSS构建跨平台桌面应用程序的框架。Electron内嵌了Chromium和Node.js,使得开发者可以使用Web技术和原生API来创建丰富的用户界面和功能。Electron的架构分为两种进程:主线程和渲染线程。本文将介绍这两种进程的作用,以及它们如何与页面进行相互调度。

主线程

主线程是Electron应用程序的入口点,它负责创建和管理应用程序窗口,以及处理与操作系统的交互。主线程可以使用Electron提供的main process API来控制应用程序的生命周期,自定义菜单栏,弹出对话框,注册全局快捷键等。主线程也可以使用Node.js的API来访问文件系统,网络,数据库等资源。主线程只有一个,它运行在主进程中。

以下是一个简单的Electron主线程代码示例:

const { app, BrowserWindow, Menu, dialog } = require('electron'); // 引入Electron模块

let mainWindow = null; // 声明主窗口变量

function createWindow() {
  mainWindow = new BrowserWindow({ width: 800, height: 600 }); // 创建主窗口
  mainWindow.loadFile('index.html'); // 加载窗口内容
  mainWindow.on('closed', () => { mainWindow = null; }); // 监听窗口关闭事件,释放窗口资源
}

app.on('ready', () => {
  createWindow(); // 应用程序准备就绪后创建主窗口

  const menuTemplate = [ // 定义应用程序菜单栏模板
    {
      label: 'File',
      submenu: [
        { label: 'Open...', accelerator: 'CmdOrCtrl+O', click: () => { // 点击菜单项打开文件选择对话框
          dialog.showOpenDialog(mainWindow, { properties: ['openFile'] });
        }},
        { label: 'Exit', accelerator: 'CmdOrCtrl+Q', click: () => { app.quit(); }} // 点击菜单项退出应用程序
      ]
    }
  ];

  const menu = Menu.buildFromTemplate(menuTemplate); // 创建菜单栏对象
  Menu.setApplicationMenu(menu); // 设置应用程序菜单栏
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') { app.quit(); } // 监听所有窗口关闭事件,退出应用程序
});

app.on('activate', () => {
  if (mainWindow === null) { createWindow(); } // 监听应用程序激活事件,如果主窗口不存在则创建它
});

渲染线程

渲染线程是Electron应用程序中负责显示页面内容的进程,它可以使用Electron提供的renderer process API来与主线程通信,调用原生模块,或者使用Web API来创建动态的用户界面。渲染线程可以有多个,每个渲染线程运行在一个独立的渲染进程中,与浏览器中的标签页类似。每个渲染进程都有自己的内存空间和事件循环,这样可以保证页面之间不会相互影响或干扰。

以下是一个在渲染进程中使用renderer process API与主线程通信的示例代码:

// 在渲染进程中,可以使用remote模块来访问主线程提供的API
const { remote } = require('electron');

// 获取当前窗口对象
const currentWindow = remote.getCurrentWindow();

// 在渲染进程中发送事件给主线程
currentWindow.webContents.send('message', 'Hello from renderer process!');

// 在渲染进程中监听来自主线程的事件
const { ipcRenderer } = require('electron');
ipcRenderer.on('message', (event, message) => {
  console.log(message); // 打印输出 "Hello from main process!"
});

还有哪些交互方式

Electron应用程序中的页面可以通过多种方式进行相互调度,例如:

  • 使用IPC(Inter-Process Communication)模块来在主线程和渲染线程之间发送和接收消息。

    在渲染进程中,可以使用ipcRenderer模块向主进程发送异步或同步消息,也可以接收主进程发送过来的消息,示例如下:

    // 在渲染进程中发送异步消息
    const { ipcRenderer } = require('electron')
    
    ipcRenderer.send('message', 'Hello from the renderer process!')
    
    // 在渲染进程中接收消息
    ipcRenderer.on('reply', (event, arg) => {
      console.log(arg) // prints "Hello from the main process!"
    })
    

    在主进程中,可以使用ipcMain模块来接收渲染进程发送的消息,并发送回复消息,示例如下:

    // 在主进程中接收消息
    const { ipcMain } = require('electron')
    
    ipcMain.on('message', (event, arg) => {
      console.log(arg) // prints "Hello from the renderer process!"
      event.reply('reply', 'Hello from the main process!')
    })
    
  • 使用remote模块来在渲染线程中直接调用主线程中的对象和方法。(本文已经举过例子了)

  • 使用webContents模块来在主线程中操作渲染线程中的页面内容,例如加载URL,执行JavaScript,截图等。

    // 在主进程中加载URL
    const { BrowserWindow } = require('electron')
    
    const win = new BrowserWindow({ width: 800, height: 600 })
    
    win.loadURL('https://www.google.com')
    
    // 在主进程中执行JavaScript
    win.webContents.executeJavaScript('alert("Hello from the main process!")')
    
    // 在主进程中截图
    win.webContents.capturePage().then((image) => {
      require('fs').writeFile('screenshot.png', image.toPNG(), (err) => {
        console.log(err)
      })
    })
    
  • 使用BrowserWindow模块来在主线程中创建和管理多个窗口,以及设置窗口之间的父子关系。

    // 在主进程中创建窗口
    const { BrowserWindow } = require('electron')
    
    const parent = new BrowserWindow({ width: 800, height: 600 })
    const child = new BrowserWindow({ width: 400, height: 300, parent })
    
    child.loadFile('child.html')
    
    // 在主进程中设置父子关系
    parent.addOwnedWindow(child)
    
  • 使用BrowserView模块来在主线程中创建和管理多个视图,以及在同一个窗口中显示不同的页面内容。

    const { BrowserView } = require('electron');
    
    const view1 = new BrowserView();
    const view2 = new BrowserView();
    
    // 将两个BrowserView添加到当前窗口
    win.setBrowserView(view1);
    win.setBrowserView(view2);
    

    然后,可以使用loadURL方法加载不同的URL到不同的BrowserView中:

    view1.webContents.loadURL('https://www.google.com');
    view2.webContents.loadURL('https://www.github.com');
    
  • 使用webview标签来在渲染线程中嵌入另一个渲染进程,并通过preload脚本和IPC来进行通信。

    首先,需要在HTML文件中添加webview标签,并设置preload属性为要加载的脚本文件:

    <webview src="https://www.baidu.com" preload="./preload.js"></webview>
    

    然后,在preload.js脚本中,可以使用window.opener来获取对父窗口的引用,并通过IPC通信:

    const { ipcRenderer } = require('electron');
    
    // 获取父窗口对象
    const parentWindow = window.opener;
    
    // 发送消息到父窗口
    ipcRenderer.sendTo(parentWindow, 'message-from-webview', 'Hello from webview!');
    

    在父窗口中,可以监听来自webview的消息,并进行相应的处理:

    const { ipcMain } = require('electron');
    
    // 监听来自webview的消息
    ipcMain.on('message-from-webview', (event, message) => {
      console.log(`Received message from webview: ${message}`);
    });
    

参考资料

转载自:https://juejin.cn/post/7229877486170767420
评论
请登录