Electron 主线程和渲染线程通信

我们在做Electron项目时,会遇到主进程与渲染进程通信的场景,那么具体怎么实现呢,我们今天用一个demo来表现下通信过程,核心业务为:项目过多,用户添加项目目标文件夹到列表,进而对项目进行统一管理,我们这里提供2种方式

效果图:

方式一:

main.js主进程,创建一个顶部菜单,打开一个文件夹,进而通知渲染进程

const { app, BrowserWindow, ipcMain, Menu, dialog, protocol } = require('electron')
let mainWindow // 保存当前的主窗口回调,用于发布消息

// 创建菜单列表,添加自定义菜单,并且快捷键为ctrl + w
let menuTemplate = [
  // 一级菜单
  {
    label: "文件",
    role: 'fileMenu',
  },
  {
    label: "视图",
    role: 'viewMenu'
  },
  {
    label: '窗口',
    role: 'windowMenu'
  },
  {
    label: '添加项目',
    click: () => {
      handle_click()
    },
    accelerator: 'ctrl + w'
  }
];

// 用于构建MenuItem
let menuBuilder = Menu.buildFromTemplate(menuTemplate);

// setApplicationMenu 在macOS上将 menu设置成应用内菜单 在windows和Linux上,menu 将会被设置成窗口顶部菜单
Menu.setApplicationMenu(menuBuilder);

function createWindow() {
  // 创建浏览器窗口
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    // fullscreen: true,
    webPreferences: {
      preload: path.resolve(__dirname, './preload.js'),
      devTools: false,
      webSecurity: false,
      enableRemoteModule: true,
      nodeIntegration : true,  //允许渲染进程使用Nodejs
      contextIsolation : false //允许渲染进程使用Nodejs
      // sandbox: false // 禁用沙盒
    },
    frame: true,
    alwaysOnTop: false
  })
}

当用户执行 ctrl + w 或者点击添加项目时,触发 handle_click方法:

// 点击添加项目
function handle_click() {
  dialog.showOpenDialog({
    title: '添加文件夹',
    properties: ['openDirectory'],
    message: '添加SVN项目'
  })
    .then(res => {
      // res.canceled为取消,反之未取消,返回filePaths物理路径
      if (!res.canceled) {
      	// 调用webContents发送消息,update_list为标识,类似订阅发布者的指定key
        mainWindow.webContents.send('update_list', res.filePaths)
      }
    })
}

preload.js 注册 ipcRenderer 方法,ipcRenderer 是一个 EventEmitter 的实例。 你可以使用它提供的一些方法从渲染进程 (web 页面) 发送同步或异步的消息到主进程。 也可以接收主进程回复的消息

// 进程间通信模块
const { contextBridge, ipcRenderer } = require('electron/renderer')

// 所有Node.js API都可以在预加载过程中使用。
// 它拥有与Chrome扩展一样的沙盒。
window.addEventListener('DOMContentLoaded', () => {
  const replaceText = (selector, text) => {
    const element = document.getElementById(selector)
    if (element) element.innerText = text
  }

  for (const dependency of ['chrome', 'node', 'electron']) {
    replaceText(`${dependency}-version`, process.versions[dependency])
  }
})

// 注册
window.electron = {
  ipcRenderer
}

最后在 renderer.js 监听消息

const child_process = require('child_process')

window.electron.ipcRenderer.on('update_list', (_event, value) => {
	// 调用node子进程
	child_process.exec(`TortoiseProc.exe /command:commit /path ${value}`, (err, stdout, stderr) => {
		console.log(stdout)
	})
}

index.html中引用renderer.js,最终将渲染指向index.html

  // 判断当前的环境,如果是开发环境,反之生产环境
  if (!app.isPackaged) {
    mainWindow.loadURL(path.resolve(__dirname, './dist/index.html'))
  } else {
    mainWindow.loadURL(path.resolve(__dirname, './dist/index.html'))
  }

方式二:

其次就是利用 http 服务,搭建一个服务器,通过请求的方式通知,这里就不过多说了,直接上代码

let serve = express()
let http = require('http').Server(serve)

//设置CORS
serve.all('*',function (req, res, next) {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, PUT');
  res.header('Access-Control-Allow-Headers', 'Content-Type');
  res.header('Access-Control-Allow-Credentials','true');
  next();
});

serve.get('/', (req, res) => res.send('Hello'))

serve.get('/file', (req, res) => {
	// 调用选择文件夹方法,并将结果返回
})

// 这里必须http监听 否则客户端会报404
let server = http.listen(8009, '127.0.0.1', () => {
  let host = server.address().address
  let port = server.address().port

  console.log(`Server running at http://${host}:${port}`)
})

你可能感兴趣的:(Javascript,Node.js,Electron,electron)