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

一、使用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、多预渲染脚本打包

· electron.vite.config.mjs
export default defineConfig({
	preload: {
		build: {
			lib: {
				entry: ['src/preload/index', 'src/preload/iframe'],
			},
		},
	},
})

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

检查 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桌面客户端

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Electron是一个基于Chromium和Node.js的框架,可以帮助开发使用Web技术(HTML,CSS,JavaScript)来构建平台桌面应用程序。VueCli是一个基于Vue.js的命令行工具,可以帮助开发者快速构建Vue.js项目。使用Electron7+VueCli4开发平台桌面应用的过程如下: 1. 安装Node.js和npm 在安装Electron和VueCli之前,需要先安装Node.js和npm。Node.js是一个基于Chrome V8引擎的JavaScript运行环境,npm是Node.js的包管理器。安装Node.js和npm可以让你在本地开发环境中运行和管理Electron和VueCli。 2. 安装Electron 在安装Electron之前,需要先创建一个空目录作为项目根目录,然后在命令行中切换到该目录下,执行以下命令: ``` npm init -y npm install electron --save-dev ``` 以上命令会创建一个package.json文件,并将Electron安装到本地项目中。 3. 创建Electron应用 在项目根目录下创建一个名为main.js的文件,这是Electron应用的主进程文件。在main.js中添加以下代码: ```javascript const { app, BrowserWindow } = require('electron') function createWindow () { // 创建浏览器窗口 const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true } }) // 加载index.html文件 win.loadFile('index.html') } app.whenReady().then(() => { createWindow() }) ``` 以上代码创建了一个Electron窗口,并加载了一个名为index.html的文件。在项目根目录下创建一个名为index.html的文件,并添加以下代码: ```html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Hello World!</title> </head> <body> <h1>Hello World!</h1> </body> </html> ``` 以上代码创建了一个包含“Hello World!”文本的HTML页面。 4. 安装VueCli 在命令行中执行以下命令安装VueCli: ``` npm install -g @vue/cli ``` 以上命令会全局安装VueCli。 5. 创建Vue.js应用 在项目根目录下执行以下命令创建一个Vue.js应用: ``` vue create renderer ``` 以上命令会创建一个名为renderer的子目录,并在其中初始化一个Vue.js项目。 6. 集成Vue.js应用 在main.js中添加以下代码将Vue.js应用集成到Electron应用中: ```javascript const { app, BrowserWindow } = require('electron') const path = require('path') function createWindow () { // 创建浏览器窗口 const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, preload: path.join(__dirname, 'preload.js') // 加载preload.js文件 } }) // 加载renderer/index.html文件 win.loadFile(path.join(__dirname, 'renderer', 'dist', 'index.html')) } app.whenReady().then(() => { createWindow() }) ``` 以上代码添加了一个preload.js文件,并将Vue.js应用的入口文件设置为renderer/dist/index.html。 7. 编译Vue.js应用 在renderer目录下执行以下命令编译Vue.js应用: ``` npm run build ``` 以上命令会生成一个名为dist的子目录,并在其中包含一个编译后的Vue.js应用。 8. 运行应用 在命令行中执行以下命令启动Electron应用: ``` npm start ``` 以上命令会启动Electron应用,并在Electron窗口中显示“Hello World!”文本和一个Vue.js应用。通过Electron7+VueCli4开发平台桌面应用,你可以使用Web技术来构建平台桌面应用程序,为用户提供更好的体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值