Electron集成发布包可以直接用dist下的html即loadFile,如果本地测试+VUE,则可以loadUrl,实际与PyWebview类似,均可以搭配一个嵌入式服务运行程序,也就是套壳。
实际上这个模版只是帮助我们把Vite和Electron整合了而已,实际开发是可以分离的,比如我更倾向于使用uni-app开发,这样可以同时兼顾微信小程序,PC网站,PC客户端,把API部分的实现单独隔离出来,比如网站和小程序使用Serverless,PC使用Electron或者Python。
Electron网络请求对比
推荐使用原生的net模块
// Issue HTTP/HTTPS requests using Chromium's native networking library.
//
// The net module is a client-side API for issuing HTTP(S) requests. It
// is similar to the HTTP and HTTPS modules of Node.js but uses Chromium's
// native networking library instead of the Node.js implementation, offering
// better support for web proxies.
//
// For more info, see:
// https://electronjs.org/docs/api/net
const { app, net } = require('electron')
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()
})
// Issue HTTP/HTTPS requests using Chromium's native networking library.
//
// The net module is a client-side API for issuing HTTP(S) requests. It
// is similar to the HTTP and HTTPS modules of Node.js but uses Chromium's
// native networking library instead of the Node.js implementation, offering
// better support for web proxies.
//
// For more info, see:
// https://electronjs.org/docs/api/net
const { app, net } = require('electron')
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()
})
Electron提供了多种方式进行网络请求,包括使用Node.js的 http
/https
模块、第三方库(如 axios
或 node-fetch
)以及Electron自己的 net
模块。这些方法之间存在一些关键的区别和优势:
1. Node.js 的 http
/https
模块
- 优势:作为Node.js的核心模块,这些模块不需要额外安装,且与Node.js环境高度集成。
- 限制:不支持某些Chromium特有的网络功能,如自动处理代理服务器配置、使用Chromium的缓存等。
2. 第三方库(如 axios
、node-fetch
)
- 优势:提供更简洁、现代的API。很多库支持Promise,使得异步代码更易于编写和维护。
- 限制:需要额外安装这些库。它们通常依赖于Node.js的网络实现,并不一定能完全利用Chromium的网络特性。
3. Electron 的 net
模块
- 优势:
- 使用Chromium的网络栈,提供对web代理和其他浏览器级别网络特性的支持。
- 可以利用Chromium的缓存机制,提高性能。
- 更好地与Electron的其他浏览器功能集成,如处理Cookies、HTTP/2支持等。
- 限制:
- API可能不如Node.js的
http
/https
或某些第三方库那么熟悉。 - 只在Electron环境中可用,不适合那些需要在纯Node.js环境下运行的代码。
- API可能不如Node.js的
总体来说,如果你需要更紧密地集成Chromium的网络特性,或者你的应用依赖于Electron的浏览器环境(如处理复杂的代理配置、Cookie管理等),使用 net
模块可能是更好的选择。否则,Node.js的 http
/https
或者像 axios
这样的第三方库可能会更简单、更方便。下面是对比的继续:
使用场景和适应性
Node.js
http
/https
:- 当需要Node.js环境的兼容性(例如,代码同时用于Electron和服务器端)时,这些模块是好的选择。
- 适合那些需要直接控制底层网络请求细节的场景。
第三方库(如
axios
、node-fetch
):- 当你需要更现代、简洁的API,以及额外的功能(如拦截器、转换响应等)时。
- 当你的项目已经在使用这些库,或者团队对它们更熟悉时。
Electron
net
模块:- 当应用需要紧密集成Electron环境,利用Chromium的网络栈的特性(如更好的代理支持、HTTP/2等)时。
- 当你的应用主要运行在Electron环境中,并且你想最大限度地利用Electron提供的功能时。
性能和效率
Electron
net
模块 由于使用Chromium的网络栈,可能在处理某些网络请求时更高效,特别是在涉及到复杂的浏览器级功能(如处理Cookies、代理设置)时。Node.js
http
/https
和 第三方库 在普通的HTTP请求处理上可能没有明显的性能差异,但在特定场景下(如大量数据传输、高并发请求)可能需要特别优化。
总结
选择哪种方法取决于你的具体需求、应用的架构以及开发团队的熟悉程度。如果你的应用需要紧密集成Electron和Chromium的特性,net
模块是一个很好的选择。如果你更倾向于使用更现代的API或者需要更好的Node.js兼容性,那么 http
/https
模块或第三方库可能更合适。
BrowserView与BrowserWindow.loadURL相比
BrowserView
是 Electron 中的一个高级功能,它提供了一种将多个独立的 web 内容嵌入到同一个窗口的能力。虽然使用 BrowserWindow
的 loadURL
或 loadFile
方法可以加载单个页面,但 BrowserView
使得在单个 BrowserWindow
中同时展示多个 web 页面或视图成为可能。下面是 BrowserView
的一些关键用途和优点:
1. 多视图布局
BrowserView
允许你在同一个窗口中布置多个独立渲染和控制的 web 视图。- 这对于需要同时显示多个网页或应用部分的复杂应用特别有用,例如一个集成了多个服务的仪表盘。
2. 更灵活的控制
BrowserView
可以被动态创建、销毁、隐藏和显示,提供了比单一页面更多的灵活性。- 它允许开发者对每个视图进行更精细的控制,比如独立设置视图大小、位置和 z-index。
3. 隔离和安全
- 不同的
BrowserView
可以彼此隔离,提高应用的安全性和稳定性。 - 如果一个
BrowserView
崩溃,它不会影响到应用中的其他部分。
4. 性能考虑
- 对于复杂的应用界面,使用多个
BrowserView
可以比使用单个BrowserWindow
加载多个 iframe 更有效率。 - 它允许每个视图独立管理和优化,有助于提高整体应用性能。
使用示例
一个典型的使用场景可能是一个集成了多个独立功能模块的应用,比如一个同时显示聊天界面、电子邮件列表和日历的办公应用。每个部分可以是一个独立的 BrowserView
,允许用户在不切换窗口的情况下交互和查看不同的信息。
总的来说,BrowserView
提供了一种在单个 Electron 窗口中展示和管理多个 web 视图的强大方法。它对于开发复杂和高度交互的桌面应用特别有用。
IPC通信-ipcRenderer.invoke和ipcRenderer.send区别
省流结论:
- ipcRenderer.invoke 与 ipcMain.handle 搭配使用实现双向通信。
- ipcRenderer.send和ipcMain.on 搭配使用实现单向通信。当然,也可以实现双向通信,只不过要通过event事件形式event.sender.send('counter-value', newValue),官方不建议。因为事件通常用于有来无回比较合适。
main.ts
,比如下面这样也是也可以双向通信的,但是不是一个最佳方式。
ipcMain.on('open-file-dialog', (event) => {
dialog.showOpenDialog({
properties: ['openFile']
}).then(result => {
if (!result.canceled && result.filePaths.length > 0) {
event.sender.send('selected-file', result.filePaths[0]);
}
}).catch(err => {
console.log(err);
});
});
ipcMain.on('open-file-dialog', (event) => {
dialog.showOpenDialog({
properties: ['openFile']
}).then(result => {
if (!result.canceled && result.filePaths.length > 0) {
event.sender.send('selected-file', result.filePaths[0]);
}
}).catch(err => {
console.log(err);
});
});
设置 preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
selectFile: () => ipcRenderer.invoke('open-file-dialog'),
handleFileSelected: (callback) => ipcRenderer.on('selected-file', callback)
});
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
selectFile: () => ipcRenderer.invoke('open-file-dialog'),
handleFileSelected: (callback) => ipcRenderer.on('selected-file', callback)
});
main.vue
<template>
<div>
<button @click="selectFile">选择文件</button>
<div v-if="filePath">选择的文件路径: {{ filePath }}</div>
</div>
</template>
<script>
export default {
data() {
return {
filePath: ''
};
},
methods: {
async selectFile() {
const path = await window.electronAPI.selectFile();
if (path) {
this.filePath = path;
// 在这里调用你的通知方法,如 this.$notify(...)
}
}
},
created() {
window.electronAPI.handleFileSelected((event, path) => {
this.filePath = path;
// 弹出通知
});
}
};
</script>
<template>
<div>
<button @click="selectFile">选择文件</button>
<div v-if="filePath">选择的文件路径: {{ filePath }}</div>
</div>
</template>
<script>
export default {
data() {
return {
filePath: ''
};
},
methods: {
async selectFile() {
const path = await window.electronAPI.selectFile();
if (path) {
this.filePath = path;
// 在这里调用你的通知方法,如 this.$notify(...)
}
}
},
created() {
window.electronAPI.handleFileSelected((event, path) => {
this.filePath = path;
// 弹出通知
});
}
};
</script>
可以看出用上面的双向通信,比较繁琐,还需要定义事件回调用来接收信息,不建议!
ipcRenderer.invoke
和 ipcRenderer.send
是 Electron 中用于进程间通信(IPC)的两种不同方法,它们在使用方式和用途上有所区别:
ipcRenderer.invoke
- 用途:
ipcRenderer.invoke
用于异步发送一条消息到主进程,并期待一个响应。这个方法通常用于当渲染进程需要从主进程获得数据或结果时。 - 返回值:这个方法返回一个
Promise
,这个Promise
解析的值是主进程通过ipcMain.handle
注册的处理函数返回的值。 - 例子:javascript
ipcRenderer.invoke('some-channel', arg1).then((result) => { // 处理结果 });
ipcRenderer.invoke('some-channel', arg1).then((result) => { // 处理结果 });
- 主进程中的处理:在主进程中,你需要使用
ipcMain.handle
来响应invoke
请求:javascriptipcMain.handle('some-channel', async (event, arg1) => { // 返回处理结果 return someResult; });
ipcMain.handle('some-channel', async (event, arg1) => { // 返回处理结果 return someResult; });
ipcRenderer.send
- 用途:
ipcRenderer.send
用于异步发送一条消息到主进程,但不期待一个直接的响应。这个方法用于单向通信,例如通知主进程某件事情发生了,而不需要主进程回应。 - 返回值:这个方法不返回值。如果需要响应,主进程必须通过
ipcRenderer.on
或者ipcRenderer.once
另外发送一个消息。 - 例子:javascript
ipcRenderer.send('some-channel', arg1);
ipcRenderer.send('some-channel', arg1);
- 主进程中的处理:在主进程中,你使用
ipcMain.on
来监听消息:javascriptipcMain.on('some-channel', (event, arg1) => { // 处理收到的消息 });
ipcMain.on('some-channel', (event, arg1) => { // 处理收到的消息 });
推荐使用哪一个?
选择使用 invoke
还是 send
取决于你的具体需求:
如果你需要一个请求-响应模式,比如从主进程请求数据并等待返回结果,那么
ipcRenderer.invoke
和ipcMain.handle
是更合适的选择。它们提供了一种更清晰和更易于管理的方式来处理异步请求。如果你仅需要通知主进程某件事情发生,并且不需要主进程立即回应,
ipcRenderer.send
和ipcMain.on
则更适合。这适用于不需要立即结果的单向通信场景。
在许多情况下,invoke
/handle
提供了更简洁和直接的方式来处理需要响应的异步操作,因此在需要双向通信时通常更被推荐。然而,对于简单的事件通知或者不需要立即响应的情况,send
/on
则足够且更简单。