electron基本教程

1. 应用基础

入口main.js,在app中启动一个window加载preload.js和html文件,然后html中添加render.js用于渲染。
定义preload.js是用于渲染页面和主程序进行交互,使用contextBridge将nodejs的api暴露给渲染页面),主要代码如下:

app.whenReady().then(() =>{
	const mainWindow = new BrowserWindow({webPreferences: {preload: path.join(__dirname, 'preload.js')}})
	mainWindow.loadFile('index.html')
	}

BrowserWindow中可以继续用BrowserView来加载外部网址:

const view = new BrowserView()
win.setBrowserView(view)
view.webContents.loadURL('https://electronjs.org')

2. main与render的交互

搞这么复杂的原因就是浏览器的js页面不允许加载js库,很多复杂的功能实现不了,必须得在main.js中实现。
需要preload.js的原因是,渲染进程中不允许直接使用ipcRender;
需要渲染js的原因是:preload.js无法操纵页面元素
真是麻了。总结一下:

  1. html中使用<script src="render.js"></script>加载页面事件监听和元素操纵的代码
  2. preload.js使用contextBridge.exposeInMainWorld暴露接口给render.js,在render.js中使用window.接口进行调用
  3. main.js相当于后端代码,除了初始化app、创建window等外,还可以创建一系列函数,提供给preload.js进行使用。

使用ipcMain & ipcRender。ipcRenderer提供方法从渲染进程 (web 页面) 发送同步或异步的消息到主进程。ipcMain则在主进程接受消息并进行处理,也可以将结果返回给渲染进程。
示例用的交互代码如下:

## preload.js
window.addEventListener('DOMContentLoaded', () => {
  const replaceText = (selector, text) => {
    const element = document.getElementById(selector)
    if (element) element.innerText = text
  }

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

渲染页面通过如下代码加载渲染用的js代码:

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

官方的electron fiddle示例跑出来也有问题,这里总结一下能成功运行的方法。

2.1 同步:使用ipcRender.sendSync和ipcMain.on(event.returnValue)

示例如下:

// render.js
const { ipcRenderer } = require('electron');
function sendSyncMessageToMain() {
  const replyMessage = ipcRenderer.sendSync('render-send-sync-to-main', '我是渲染进程通过 syncSend 发送给主进程的消息');
  console.log('replyMessage', replyMessage); // '主进程回复的消息'
}

// main.js
const { ipcMain } = require('electron');
ipcMain.on('render-send-sync-to-main', (event, message) => {
  console.log(`receive message from render: ${message}`)
  event.returnValue = '主进程回复的消息';
})

2.2 异步:使用ipcRender.invoke和ipcMain.handle(return)

渲染进程异步等待主进程的回应, invoke 的返回值是一个 Promise 。我们可以不使用 await 来接收 invoke 的返回结果,打印的结果符合我们的预期,是一个 Promise。

// render.js
const { ipcRenderer } = require('electron');
async function sendAsyncMessageToMain() {
  const replyMessage = await ipcRenderer.invoke('render-invoke-to-main', '我是渲染进程通过 invoke 发送的消息');
  console.log('replyMessage', replyMessage);
}]

// main.js
const { ipcMain } = require('electron');
ipcMain.handle('render-invoke-to-main', async (event, message) => {
  console.log(`receive message from render: ${message}`)
  const result = await asyncWork();
  return result;
})

3. 综合示例

下面是使用highs.js进行线性规划求解的一个例子,效果如图:
在这里插入图片描述

3.1 后台函数main.js

定义求解函数

const highs_promise = require("highs")();
const { app,BrowserWindow, ipcMain } = require('electron');
const path = require('node:path')
let win;
function createWindow() {
    win = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: true,
            preload: path.join(__dirname, 'preload.js')
        }
    });
    win.loadFile('index.html');
    win.on('closed', () => {win = null;});
}

app.whenReady().then(() => {
    createWindow();
    ipcMain.handle('solve-lp', async (event, data) => {   
        const highs = await highs_promise
        return highs.solve(data)
        })
})

3.2 接口封装preload.js

const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
  solve: async (data) => {return await ipcRenderer.invoke('solve-lp', data)}
})

3.3 前端函数render.js

添加按钮事件,将求解结果绑定到dom元素上

document.getElementById('solve-btn').addEventListener('click', (event) => {
    event.preventDefault()
    const promise = window.electronAPI.solve(document.getElementById('Problem').value)
    promise.then(res => {document.getElementById('result').innerHTML = JSON.stringify(res, null, "  ")})
})

3.4 网页文件index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Linear Programming Solver</title>
</head>
<body>
    <h1>线性规划求解器</h1>
    <h2>输入问题</h2>
    <form id="linear-form">
        <textarea id="Problem" name="Problem" rows="10" cols="80">
Maximize obj:x1 + 2 x2 + 4 x3 + x4
Subject To
c1: - x1 + x2 + x3 + 10 x4 <= 20
c2: x1 - 4 x2 + x3 <= 30
c3: x2 - 0.5 x4 = 0
Bounds
0 <= x1 <= 40
2 <= x4 <= 3
End</textarea><br><br>
        <button id="solve-btn">求解</button>
    </form>
    <div id="result"></div>
    <script src="linear-form.js"></script>
</body>
</html>

3.5 项目配置文件package.json

{
  "name": "test",
  "version": "1.0.0",
  "description": "test",
  "main": "main.js",
  "scripts": {
    "start": "electron .",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "test",
  "license": "ISC",
  "dependencies": {"highs": "^1.8.0"}
}

使用cnpm install highs --save安装好highs,然后执行cnpm start就可以运行程序啦。

4. 高级功能

autoUpdater

const { app, autoUpdater } = require('electron/main')
app.whenReady().then(() => {
  const server = 'https://your-deployment-url.com'
  const feed = `${server}/update/${process.platform}/${app.getVersion()}`
  try {
    autoUpdater.setFeedURL(feed)
  } catch (error) {
    console.log(error)
  }
})

clipboard

main.js中的函数如下:

const { app, BrowserWindow, ipcMain, clipboard } = require('electron/main')
ipcMain.handle('clipboard:readText', () => {
  return clipboard.readText()
})
ipcMain.handle('clipboard:writeText', (event, text) => {
  clipboard.writeText(text)
})

通过preload.js提供给html文件:

const { contextBridge, ipcRenderer } = require('electron/renderer')
contextBridge.exposeInMainWorld('clipboard', {
  readText: () => ipcRenderer.invoke('clipboard:readText'),
  writeText: (text) => ipcRenderer.invoke('clipboard:writeText', text)
})

然后在render.js中定义html的页面渲染函数:

window.addEventListener('DOMContentLoaded', () => {
  const copyButton = document.querySelector('#copy')
  const pasteButton = document.querySelector('#paste')
  const textarea = document.querySelector('textarea')
  copyButton.onclick = () => {
    window.clipboard.writeText('Hello from Electron!') # 向剪贴板中写入text
  }
  pasteButton.onclick = async () => {
    textarea.value = await window.clipboard.readText()
  }
})

文件对话框

const { filePaths, canceled } = await dialog.showOpenDialog(mainWindow, {properties: ['openFile']})

菜单栏

  const menu = Menu.buildFromTemplate(template)
  Menu.setApplicationMenu(menu)

网络

const { app, net } = require('electron/main')

app.whenReady().then(() => {
  const request = net.request('https://github.com')

  request.on('response', (response) => {
    console.log(`STATUS: ${response.statusCode}`)
    console.log(`HEADERS: ${JSON.stringify(response.headers)}`)

    response.on('data', (chunk) => {
      console.log(`BODY: ${chunk}`)
    })

    response.on('end', () => {
      console.log('No more data in the response.')
    })
  })

  request.end()
})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值