4.electron中的Node.js和进程间的通信

2,003 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情

使用Node.js 模块/API

相信用过Node.js的小伙伴来说,对Node中的fs一定不陌生,今天通过读写文件的例子来介绍下electron中的Node.js。

首先需要在主进程文件中开启node:

odeIntegration: truecontextIsolation: false

主进程代码如下:

// main.js

const { app, BrowserWindow } = require('electron')
app.on('ready', () => {
  // 新建一个窗口
  app.mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true, // 是否集成 Nodejs
      contextIsolation: false,
    },
  })
  // 加载渲染文件
  app.mainWindow.loadFile('./index.html')
  // 窗口关闭后清空变量
  app.mainWindow.on('close', () => {
    mainWindow = null
  })
})

渲染进程代码如下:

// index.html
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>读写文件测试</title>
  </head>
  <body>
    <button onclick="readFile()">读取文件</button>
    <button onclick="writeFile()">写入文件</button>
    <p id="show_file_content">页面内容</p>
    <script src="./testFile.js"></script>
  </body>
</html>

新建一个testFile.js文件进行文件的读取和写入:

// 导入 node 的模块
const fs = require('fs')
const path = require('path')
const { log } = console

// 获取到文件展示的dom
const showContent = document.getElementById('show_file_content')

// 读取文件
function readFile() {
  console.log('读取文件')
  fs.readFile(path.join(__dirname, '/msg.txt'), (err, data) => {
    if (err) {
      throw new Error(err, '读取文件失败')
    }
    showContent.innerText = data
  })
}
// 需要写入的内容
const content = '我今天学习了node.js'

// 写入文件
function writeFile() {
  fs.writeFile(
    path.join(__dirname, '/msg.txt'),
    content,
    'utf8',
    (err, data) => {
      if (err) {
        return new Error(err, '读取文件失败')
      }
      log('写入文件成功')
    }
  )
}

新建测试文件msg.txt,内容为:“你今天学了什么”

最终效果如下:

20220410_200641.gif

进程间的通信

进程间的通信可以分为以下三种:

主进程渲染进程:通过 webContents.send 来发送 --->ipcRenderer.on 来监听

渲染线程主线程:通过 ipcRenderer.send发送 ---> ipcMain.on来监听

渲染进程渲染进程:通过主进程转发或者通过ipcRenderer.sendTo指定渲染进程id进行发送

1. 主进程到渲染进程

webContents.send(channel, ...args)

  • channel String
  • ...args any[]

主进程 mian.js

在主进程中使用 webContents.send 发送消息:

var { app, ipcMain, BrowserWindow } = require('electron');
const HTray = require('./tray').HTray
// 引入menu
require('./menu')
app.on('ready', () => {
  app.win = new BrowserWindow({
    width: 500,
    height: 300,
    webPreferences: {
      nodeIntegration: true,
      enableRemoteModule:true, // 启用remote
      contextIsolation: false
    }
  })

  
  // 发送给渲染线程
  setTimeout(() => {
    app.win.webContents.send('mainMsg', '我是主线程发送的消息')
  }, 3000)

 
  app.win.loadFile('index.html');
  app.win.webContents.openDevTools();

  // 监听关闭事件
  app.win.on('close', (event) => {
    // 1.隐藏窗口
    app.win.hide(); 
    // 2.隐藏任务栏
    app.win.setSkipTaskbar(true);
    // 3.阻止默认行为(因为并不是要关闭)
    event.preventDefault();
  })
})
app.on('window-all-closed', () => {
  app.quit();
})

渲染进程 main.html 外链一个 render.js

在渲染线程中使用 ipcRenderer.on来进行监听

ipcRenderer.on(channel, listener)
  • channel String
  • listener Function
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>electron_demo</title>
</head>
<style>
</style>
<body>
  通信测试
  <p id="receive">接收信息</p>
  <script src="./render.js"></script>
</body>
</html>


// render.js
const electron = require('electron')
const { ipcRenderer } = require('electron')
const { log } = console

log(ipcRenderer)
ipcRenderer.on('mainMsg', (event, task) => {
  log(task)
  document.getElementById('receive').innerText = task
})

效果如下:

20220410_204130.gif

2. 渲染进程到主进程

在上一篇咱们写了一个托盘闪烁的功能,就是通过渲染进程给主进程发消息,主进程进行接收信号并处理的方法来实现的:

// index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>electron_demo</title>
</head>
<style>
</style>
<body>
  <div>闪烁测试</div>
  <button onclick="start()">开始闪烁</button>
  <button onclick="stop()">停止闪烁</button>

  <script>
    const { ipcRenderer } = require('electron');
    // 点击闪烁
    function start() {
      ipcRenderer.send("startFlash");
    }

    // 停止闪烁
    function stop() {
      ipcRenderer.send("stopFlash");
    }
  </script>
</body>
</html>

主进程:

// index.js

var { app, ipcMain, BrowserWindow } = require('electron');
const HTray = require('./tray').HTray
// 引入menu
require('./menu')
app.on('ready', () => {
  app.win = new BrowserWindow({
    width: 500,
    height: 300,
    webPreferences: {
      nodeIntegration: true,
      enableRemoteModule:true, // 启用remote
      contextIsolation: false
    }
  })

  // 创建系统托盘
  app.tray = new HTray();
  // 显示系统托盘
  app.tray.show();

  // 托盘闪烁
  ipcMain.on('startFlash', (event, arg) => {
    app.tray.startFlash();
  })

  // 停止闪烁
  ipcMain.on('stopFlash', (event, arg) => {
    app.tray.stopFlash();
  })

  app.win.loadFile('index.html');
  app.win.webContents.openDevTools();

  // 监听关闭事件
  app.win.on('close', (event) => {
    // 1.隐藏窗口
    app.win.hide(); 
    // 2.隐藏任务栏
    app.win.setSkipTaskbar(true);
    // 3.阻止默认行为(因为并不是要关闭)
    event.preventDefault();
  })
})
app.on('window-all-closed', () => {
  app.quit();
})

具体实现方法和效果图可以看上一篇哟

3. 渲染进程到渲染进程

方法1:

ipcRenderer.sendTo(webContentsId, channel, ...args)
  • webContentsId Number
  • channel String
  • ...args any[]

通过 ipcRenderer 发送消息到带有 webContentsId 的窗口.

前提是要知道对应的渲染进程的ID

方法2:

可以让主进程作为中转站,先从渲染进程发到主进程,在通过主进程转发到其他的渲染进程;

// 渲染进程A:
ipcRenderer.send('sendMsg', '哈哈哈哈哈');

// 主进程:
ipcMain.on('sendMsg', (event, arg) => {
    win.webContents.send('sendMsgToRenderer2', arg)
})

// 渲染进程B:
ipcRenderer.on('sendMsgToRenderer2', (event, data) => {
    console.log("渲染进程B接收到的数据", data)
})

以上例子就是electron中进程间的通信啦,更多使用方法可以查看官方文档哦!!!

官方文档:

ipcMain

ipcRenderer

webContents

感谢

谢谢你读完本篇文章,希望对你能有所帮助,如有问题欢迎各位指正。

我是Nicnic,如果觉得写得可以的话,请点个赞吧❤。

写作不易,「点赞」+「在看」+「转发」 谢谢支持❤

往期好文

《Javascript高频手写题1.0》

《Vue中生成图片验证码的小组件》

《前端JS高频面试题---1.发布-订阅模式》

《前端JS高频面试题---2.单例模式》

《前端JS高频面试题---3.代理模式》

《前端JS高频面试题---4.策略模式》

《前端CSS高频面试题---1.CSS选择器、优先级、以及继承属性》

《前端CSS高频面试题---2.em/px/rem/vh/vw的区别》

《前端CSS高频面试题---3.如何实现两栏布局,右侧自适应?三栏布局中间自适应呢?》