Electron - 跨平台桌面应用开发工具的使用总结

文章目录

    • 一、使用electron-vite新建项目
    • 二、目录结构
    • 三、渲染进程调用主进程
      • 1、方式一 —— 允许有返回值
        • · src/main/index.js
        • · src/preload/index.js
        • · src/renderer/index.html
      • 2、方式二—— 允许有返回值 (推荐写法)
        • · src/main/index.js
        • · src/preload/index.js
        • · src/renderer/index.html
      • 3、方式三 —— 无返回值,不等待响应
        • · src/main/index.js
        • · src/preload/index.js
        • · src/renderer/index.html
    • 四、主进程触发渲染进程
      • · src/main/index.js
      • · src/preload/index.js
      • · src/renderer/index.html
    • 五、功能介绍
      • 1、透明窗口设置
      • 2、系统截图功能
      • 3、系统托盘图标及闪烁功能
    • 六、问题处理
      • 1、图标打包路径无法获取问题
        • · 修改图标路径获取
      • 2、允许更改安装目录
        • · package.json 配置
      • 3、loadFile添加参数
      • 4、http数据请求与cookie设置
      • 5、iframe CSP
      • 6、多页面打包
        • · electron.vite.config.mjs
      • 7、路由切换主进程代码多次执行
    • 七、npm库
    • References

一、使用electron-vite新建项目

  • 1、npm命令 npm create @quick-start/electron
  • 2、yarn命令 yarn create @quick-start/electron
  • 3、electron镜像地址:
electron_mirror=https://npmmirror.com/mirrors/electron/
electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/
  • 4、填写项目名称
? Project name: electron-vue-app 
  • 5、选择vue框架:
? Select a framework: 
    vanilla
>   vue
    react
    svelte
    solid
  • 6、其他配置项
? Add TypeScript? » No / Yes   
Yes
? Add Electron updater plugin? » No / Yes 
Yes
? Enable Electron download mirror proxy? » No / Yes
Yes

二、目录结构

├─ /.vscode
├─ /build
├─ /node_modules
├─ /out                         # 运行时的输出目录
├─ /resources                   # 主进程和预加载脚本资源文件目录
├─ /src
|  ├─ /main                     # Electron主进程
|  ├─ /preload                  # Electron预加载脚本
|  └─ /renderer                 # Electron渲染进程, vue常规目录结构
|  |  ├─ /assets
|  |  ├─ /components
|  |  ├─ /views
|  |  ├─ App.vue
|  |  ├─ main.js
|  |  └─ index.html
├─ .editorconfig
├─ .eslintignore
├─ .eslintrc.cjs
├─ .gitignore
├─ .npmrc
├─ .prettierignore
├─ .prettierrc.yaml
├─ dev-app-update.yml
├─ electron-builder.yml
├─ electron.vite.config.mjs
├─ package.json
├─ README.md

三、渲染进程调用主进程

1、方式一 —— 允许有返回值

· src/main/index.js
import { app, ipcMain, BrowserWindow } from 'electron'
app.whenReady().then(() => {
	// ...

	new BrowserWindow({
        width: 800,
        height: 600,
        // 其他窗口配置...
    })
	
    ipcMain.handle('renderderCallMain', async (event, value) => {
        console.log('renderder call main...', value)
    })

	// ...
})
· src/preload/index.js
import { contextBridge } from 'electron'
import { electronAPI } from '@electron-toolkit/preload'
if (process.contextIsolated) {
	try {
		contextBridge.exposeInMainWorld('electron', electronAPI)
	} catch (error) {
		console.error(error)
	}
} else {
	window.electron = electronAPI
}
· src/renderer/index.html
window.electron.ipcRenderer.invoke('renderderCallMain', 'hello world!')

2、方式二—— 允许有返回值 (推荐写法)

· src/main/index.js
import { app, ipcMain, BrowserWindow } from 'electron'
app.whenReady().then(() => {
	// ...

	new BrowserWindow({
        width: 800,
        height: 600,
        // 其他窗口配置...
    })
	
    ipcMain.handle('renderderCallMain', async (event, value) => {
        console.log('renderder call main...', value)
    })

	// ...
})
· src/preload/index.js
import { contextBridge, ipcRenderer } from 'electron'

const call = {
	renderderCallMain: (value) => ipcRenderer.invoke('renderderCallMain', value),
}

if (process.contextIsolated) {
	try {
		contextBridge.exposeInMainWorld('call', call)
	} catch (error) {
		console.error(error)
	}
} else {
	window.call = call
}
· src/renderer/index.html
window.call.renderderCallMain('hello world!')

3、方式三 —— 无返回值,不等待响应

· src/main/index.js
import { app, ipcMain, BrowserWindow } from 'electron'
app.whenReady().then(() => {
	// ...

	new BrowserWindow({
        width: 800,
        height: 600,
        // 其他窗口配置...
    })
	
    ipcMain.on('ping', () => console.log('pong'))

	// ...
}
· src/preload/index.js
import { contextBridge, ipcRenderer } from 'electron'

const call = {
	ping: () => ipcRenderer.send('ping'),
}

if (process.contextIsolated) {
	try {
		contextBridge.exposeInMainWorld('call', call)
	} catch (error) {
		console.error(error)
	}
} else {
	window.call = call
}
· src/renderer/index.html
window.call.ping()

四、主进程触发渲染进程

· src/main/index.js

import { app, globalShortcut, BrowserWindow } from 'electron'
app.whenReady().then(() => {
	// ...

	const mainWindow = new BrowserWindow({
        width: 800,
        height: 600,
        // 其他窗口配置...
    })

    globalShortcut.register('CommandOrControl+S', () => {
		mainWindow.webContents.send('commandOrControlS', 'command or control + s...')
	})

	// ...
})

· src/preload/index.js

import { contextBridge, ipcRenderer } from 'electron'
const listen = {
	commandOrControlS: (callback) => ipcRenderer.on('commandOrControlS', async (event, value) => callback(value)),
}

if (process.contextIsolated) {
	try {
		contextBridge.exposeInMainWorld('listen', listen)
	} catch (error) {
		console.error(error)
	}
} else {
	window.listen = listen
}

· src/renderer/index.html

window.listen.commandOrControlS((value) => {
    console.log(value)
})

五、功能介绍

1、透明窗口设置

const mainWindow = new BrowserWindow({
    width: 80,
    height: 162,
    frame: false,                   // 透明窗口
    transparent: true,              // 透明窗口
    backgroundColor: '#00000000',   // 透明窗口
    autoHideMenuBar: true,          // 透明窗口
})

2、系统截图功能

import { desktopCapturer } from 'electron'

async function desktopCapturerHandle() {
    const sources = await desktopCapturer.getSources({
        types: ['window'],
        thumbnailSize: {
            width: 1920,
            height: 1080,
        },
    })

    return sources[0]?.thumbnail.toDataURL('image/png'),
}

3、系统托盘图标及闪烁功能

import { app, BrowserWindow, Tray, Menu } from 'electron'

let flickerTimer = null

app.whenReady().then(() => {
	// ...

	const mainWindow = new BrowserWindow({
        width: 800,
        height: 600,
        // 其他窗口配置...
    })
	
    const tray = new Tray('./icon.ico')
	const contextMenu = Menu.buildFromTemplate([
		{ label: '显示       ', click: () => {
			mainWindow.show()
			stopTray(tray)
		}},
		{ type: 'separator' },
		{ label: '退出       ', click: () => {
			mainWindow.off('close', closeHandle)
			app.quit()
		}},
	])
	tray.setContextMenu(contextMenu)
	tray.on('double-click', function() {
		mainWindow.show() // 点击图标时恢复窗口
		stopTray(tray)
	})
	tray.on('click', function() {
		if (flickerTimer) {
			mainWindow.show() // 点击图标时恢复窗口
			stopTray(tray)
		}
	})

	// ...
})

function flickerTray(tray) {
	if (!flickerTimer) {
		let hasIco = false
		flickerTimer = setInterval(() => {
            // 图标与透明图标切换以实现闪烁功能
			tray.setImage(hasIco ? './icon.ico' : './empty.ico')
			hasIco = !hasIco
		}, 500)
	}
}

function stopTray(tray) {
	if (flickerTimer) {
		clearInterval(flickerTimer)
		flickerTimer = null
	}
	tray.setImage('./icon.ico')
}

六、问题处理

1、图标打包路径无法获取问题

· 修改图标路径获取
import { app } from 'electron'
const path = require('path')

function resolvePath() {
	const publicPath = 'build'
    const fileName = 'icon.ico'
	if (process.env.NODE_ENV === 'development') {
        return './' + publicPath + '/' + fileName
	}
	return path.join(path.dirname(app.getPath('exe')), '/resources/' + publicPath + '/' + 'fileName')
}
· package.json 配置图标打包
{
    ...

    "build": {
        "extraResources": [
            {
                "from": "./build",
                "to": "./build"
            }
        ]
    }

    ...
}

图标大小需 256x256,格式为ico

2、允许更改安装目录

· package.json 配置
{
    ...

    "build": {
        "nsis": {
            "allowToChangeInstallationDirectory": true, // 允许更改安装目录
            "installerIcon": "./build/icon.ico",        // 安装图标
            "uninstallerIcon": "./build/icon.ico",      // 卸载图标
            "installerHeaderIcon": "./build/icon.ico",  // 安装程序头部图标
            "createDesktopShortcut": true,              // 创建桌面快捷方式
            "createStartMenuShortcut": false,           // 加入开始菜单
        }
    }

    ...
}

3、loadFile添加参数

import { app, BrowserWindow } from 'electron'
import { join } from 'path'

app.whenReady().then(() => {
	// ...

	const mainWindow = new BrowserWindow({
        width: 800,
        height: 600,
        // 其他窗口配置...
    })
	
    mainWindow.loadFile(join(__dirname, '../renderer/index.html'), {
        search: 'id=test',
    })

	// ...
})

4、http数据请求与cookie设置

  • 1)、由于 fill:// 协议无法设置cookie,所以请求只能在主进程发起
  • 2)、使用Chromium的原生网络库发出HTTP / HTTPS请求 或 Node.js的HTTP 和 HTTPS 模块
const { app } = require('electron')

app.whenReady().then(() => {
    const { net } = require('electron')
    const request = net.request('https://github.com')
    request.on('response', (response) => {
        console.log(response)
        response.on('data', (chunk) => {
            console.log('BODY: ' + chunk)
        })
        response.on('end', () => {
            console.log('No more data in response.')
        })
    })
    request.end()
})
  • 3)、从请求结果中 set-cookie 获取cookie并返回给渲染端进行存储

5、iframe CSP

<meta 
    http-equiv="Content-Security-Policy"
    content="script-src 'self' https://example.com; img-src https://example.com"
/>

修改html中的meta属性,这将允许在iframe中加载来自 selfexample.com 域的脚本,并允许加载来自 example.com 域的图像

6、多页面打包

· electron.vite.config.mjs
export default defineConfig({
	renderer: {
		build: {
			rollupOptions: {
				input: {
					index: './src/renderer/index.html',
					other: './src/renderer/other.html',
				}
			}
		}
	}
})

7、路由切换主进程代码多次执行

检查 ipcRenderer.on() 路由切换后是否多次在 onMounted 注册,需在 onBeforeUnmount 中及时调用主进程 ipcRenderer.removeListener() 注销事件的监听

七、npm库

  • 1、"@jitsi/robotjs": "^0.6.11" 适用于自动化工具,有控制I/O、系统截图等功能
  • 2、"tesseract.js": "^5.0.5" 基于js的图像识别
  • 3、"jimp": "^0.22.12" 图像处理工具,有图片裁剪、保存、图片大小更改等功能
  • 4、"nodemailer": "^6.9.13" 基于node的邮件发送工具

References

[1] Electron文档
[2] 基于electron-vite构建Vue桌面客户端

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/632720.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前沿动态 | 关于AI大模型,你知道多少?

AI大模型含义 AI 大模型是人工智能预训练大模型的简称&#xff0c;包含了“预训练”和“大模型”两层含义&#xff0c;二者结合产生了新的人工智能模式&#xff0c;即模型在大规模数据集上完成预训练后&#xff0c;仅需少量数据的微调甚至无需微调&#xff0c;就能直接支撑各类…

python高级爱心代码

python高级爱心代码实现&#xff1a; import turtle import random # 设置画布 screen turtle.Screen() screen.bgcolor("black") # 创建画笔 pen turtle.Turtle() pen.speed(0) pen.color("red") pen.penup() # 移动画笔到起始位置 pen.goto(0, -20…

伪头部校验

本章问题 UDP和TCP的伪首部只用于计算校验和&#xff0c;在UDP和TCP的报文中是不存在的&#xff0c;为什么要引入伪首部呢&#xff1f;为什么伪首部的要有这些字段&#xff1f;这里我们就先看一下TCP和UDP的首部格式。 TCP和UDP首部 源端口目的端口&#xff1a;是0-65535任…

代码随想录-算法训练营day41【动态规划04:01背包问题-滚动数组、分割等和子集】

代码随想录-035期-算法训练营【博客笔记汇总表】-CSDN博客 第九章 动态规划part04● 01背包问题,你该了解这些! ● 01背包问题,你该了解这些! 滚动数组 ● 416. 分割等和子集 正式开始背包问题,背包问题还是挺难的,虽然大家可能看了很多背包问题模板代码,感觉挺简单,…

2024汽车行业用户洞察与营销趋势白皮书

来源&#xff1a;小红书&寰球汽车&#xff1a;

晶振的振荡模式有哪些?

晶振&#xff0c;即晶体振荡器&#xff0c;是一种能够产生精确振荡频率的电子元件&#xff0c;它利用石英晶体的压电效应来产生稳定的振荡信号。晶振的振荡模式主要有以下几种&#xff1a;1. 串联谐振&#xff08;Series Resonance&#xff09;&#xff1a; 在这种模式下&am…

Moe 混合多专家模型 原理 + 大模型的有性繁殖 + DIY 自己的 Moe 专家系统

Moe 混合多专家模型 原理 大模型的有性繁殖 DIY 自己的 Moe 专家系统 MoE 介绍标准 Transformer 编码器MoE Transformer 编码器专家网络层 大模型的有性繁殖mergekit 合并 多个专家模型 的方式1. SLERP&#xff08;球面线性插值&#xff09;2. TIES3. DARE4. Passthrough5. L…

露营涮火锅小朋友不慎烫伤大腿 家长抱娃涮河急救获医生点赞

近日&#xff0c;陈女士一家三口在西安市长安区附近露营涮火锅&#xff0c;却不料小朋友在起身时不小心打翻了吃火锅的锅&#xff0c;导致腿被烫伤&#xff0c;陈女士急忙抱着孩子到临近河边&#xff0c;用河水来冲洗小朋友烫伤的腿&#xff0c;随后立刻赶至西安国际医学中心医…

Kroma宣布推出Spectrum:以太坊Layer-2的先进原生质押服务

Kroma宣布推出备受期待的Spectrum&#xff0c;这是一项先进的原生质押服务&#xff0c;旨在彻底改变以太坊 Layer-2格局。Spectrum将于2024年5月14日开始运营&#xff0c;为用户提供利用质押ETH、stETH和eETH的奖励机会&#xff0c;助力用户在去中心化金融&#xff08;DeFi&…

Python 海龟画图(Turtle)命令大全

移动和绘制 forward() | fd() 使用语法&#xff1a; ​​turtle.forward(距离)​​ ​​turtle.fd(距离)​​ 参数说明: 距离 一个数字 (整数 或者 浮点) &#xff08;注&#xff1a;单位是像素&#xff09; 代码示例&#xff1a; import turtle turtle.forward(200) …

docker镜像中搭建FastDfs

docker镜像中搭建FastDfs 一、搭建过程二、docker端口映射三、映射的方法三、配置Tracker 和 Storage 环境&#xff1a;腾讯云服务器上 ubuntu20.04镜像 一、搭建过程 正常直接在云服务器上搭建过程参考博客&#xff1a; https://blog.csdn.net/qq_38531706/article/details/…

分享如何通过定时任务调用lighthouse前端测试脚本+在持续集成测试中调用lighthouse前端测试脚本

最近写了个小工具来优化lighthouse在实际工作中的使用&#xff0c;具体实现了&#xff1a;通过定时任务调用前端测试脚本在持续集成测试中调用前端测试脚本。由于在公司中已经应用&#xff0c;所以就不能提供源码了&#xff0c;这里简单说一下实现思路&#xff0c;希望可以帮助…

武汉星起航:亚马逊跨境电商助力全球贸易:卖家轻松触达全球市场

作为全球最大的电子商务平台之一&#xff0c;亚马逊以其强大的品牌影响力和完善的服务体系&#xff0c;为全球卖家提供了一个展示和销售各类商品的广阔舞台。亚马逊跨境电商业务的快速发展&#xff0c;不仅为卖家打开了通向全球市场的大门&#xff0c;也为买家带来了更多元化、…

JS+JQ+Layui菜单栏点击URL跳转后保持选中状态

目录 一.问题描述 二.问题分析 三.问题解决 3.1JQuery解决 3.2原生JS解决 四.衍生问题 五.衍生问题解决方式 5.1JQ解决 5.2JS解决 六.效果 七.建议 一.问题描述 后端使用“Flask”开发 最近在开发个人网站&#xff0c;其中使用了“Layui”组件库来做“菜单导航栏…

Nginx日志格式配置

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 Nginx(“engine x”…

数据库SQL语言实战(九)(索引)

目录 1实战目标 2前提知识 2.1索引失效的情况 2.1.1数据准备 2.1.2插入数据 2.1.3失效情况 3练习题(利用索引提高查询速度&#xff09; 题目一 题目二​ 题目三​ 题目四​ 题目五​ 总结 1实战目标 对比有无索引情况下数据检索速度,学会如何能够使用索引,掌握如何…

130.哈希表:赎金信(力扣)

题目描述 代码解决 class Solution { public:bool canConstruct(string ransomNote, string magazine) {// 创建一个大小为26的整数数组&#xff0c;用于存储每个字母在magazine中的出现次数int hash[26] {0};// 遍历magazine&#xff0c;将每个字母的出现次数记录在hash数组…

The 13th Shandong ICPC Provincial Collegiate Programming Contest

The 13th Shandong ICPC Provincial Collegiate Programming Contest The 13th Shandong ICPC Provincial Collegiate Programming Contest A. Orders 题意&#xff1a;有n个订单&#xff0c; 每日可生产k个产品&#xff0c;每个订单给出交付日和交付数量&#xff0c;是否能…

谈谈【软件测试的基础知识,基础模型】

关于软件测试的基本概念和基本模型 前言一个优秀的测试人员具备的素质关于需求测试用例软件错误(BUG)概念开发模型瀑布模型&#xff08;Waterfall Model&#xff09;螺旋模型&#xff08;Spiral Model&#xff09; 前言 首先,什么是软件测试? 通俗来讲:软件测试就是找BUG&…

Vitis HLS 学习笔记--优化本地存储器访问瓶颈

目录 1. 简介 2. 代码解析 2.1 原始代码 2.2 优化后 2.3 分析优化措施 3. 总结 1. 简介 在Vitis HLS中&#xff0c;实现II&#xff08;迭代间隔&#xff09; 1是提高循环执行效率的关键。II1意味着每个时钟周期都可以开始一个新的迭代&#xff0c;这是最理想的情况&…