打包后窗口白屏

问题描述:使用electron打包后,运行程序后窗口白屏

原因

生产环境加载窗口使用的是loadFile方法,而开发环境使用的是loadURL方法,导致路径不一致,导致白屏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const { app, BrowserWindow } = require('electron')
routers.push(
new EventRoute('tool', 'event', (api, data = {}) => {
const toolWindow = new BrowserWindow({
width: 300,
height: 400,
resizable: false,
autoHideMenuBar: true,
webPreferences: {
nodeIntegration: true,
devTools: true
}
})
const winURL = process.env.NODE_ENV === 'development'
? 'http://localhost:5173/#/tool'
: `${path.join(__dirname, '../dist/index.html#tool')}`
toolWindow.loadURL(winURL)
})
)

解决方法

路由添加对应路径

1
2
3
4
5
{
path: '/tool',
name: 'Tool',
component: ()=>import('@views/components/Tool.vue')
}

路由设置hash模式

1
2
3
4
5
6
7
8
import { createRouter, createWebHashHistory } from 'vue-router'
import routes from './routes'
const router = createRouter({
history: createWebHashHistory(),
routes
})

export default router

loadURL改为loadFile

判断是否是开发环境

  • process.env.NODE_ENV === ‘development’
  • ELECTRON_RENDERER_URL 如果没有设置,在生产环境中默认undefined
1
2
3
4
5
6
7
if(process.env['ELECTRON_RENDERER_URL']) {
// 开发环境
toolWindow.loadURL(process.env['ELECTRON_RENDERER_URL']+'/#/tool')
} else {
// 生产环境
toolWindow.loadFile(path.join(__dirname, '../renderer/index.html'), {hash:'/tool'})
}

如何实现数据共享

问题描述:使用electron开发桌面应用,如何实现不同窗口之间的数据共享?

原因

electron中不同的窗口之间默认有数据隔离,导致不同窗口之间无法共享数据。

解决方案

跨窗口通信

流程:窗口b渲染进程向主进程通信,主进程窗口a渲染进程通信。

窗口b渲染进程

1
2
3
4
5
6
7
8
9
10
const { ipcRenderer } = require('electron');
const saveConfiguration = () => {
const config = JSON.stringify(form.value)
try {
ipcRenderer.send('renderer-to-main', { config, name: 'saveConfig' });
} catch (error) {
console.error('发送 IPC 消息失败:', error);
}
ElMessage.success('配置保存成功')
}

主进程监听窗口b渲染进程

1
2
3
4
5
6
const { ipcMain } = require('electron');
ipcMain.on('renderer-to-main', (event, { name, data }) => {
const config = JSON.parse(data.config)
// 向窗口a渲染进程发送数据
mainWindow.webContents.send('main-to-renderer', { config })
})

窗口a监听主进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const { ipcRenderer } = require('electron');
ipcRenderer.on('main-to-renderer', (event, { config }) => {
// 存储pinia数据
store.saveConfig(data.config)
})

//pinia
import { createPinia, defineStore } from 'pinia'
const pinia = createPinia()

export const useStore = defineStore('main', {
state: () => ({
config: JSON.parse(localStorage.getItem('config') || '{}')
}),
actions: {
saveConfig(config) {
this.config = config
localStorage.setItem('config', JSON.stringify(config))
},
}
})

export default pinia

Electron Store

描述:可以使用 Electron Store 或 SQLite 等持久化数据库来共享数据。主进程负责对存储的数据进行写入和读取,各个窗口通过 IPC 请求主进程获取或更新数据。这种方法的优点是数据可以持久化,窗口关闭后不会丢失。

注意:

  • Electron Store 仅支持 Node.js 环境,不能在渲染进程中使用。
  • 需要 Electron 30 或更高版本。

安装:

1
npm install --save electron-store

主进程

1
2
3
4
5
6
7
8
9
10
11
// 主进程中使用 electron-store
const Store = require('electron-store')
const store = new Store()

ipcMain.on('save-data', (event, data) => {
store.set('sharedData', data) // 保存数据到 Electron Store
})

ipcMain.handle('get-data', () => {
return store.get('sharedData') // 返回数据
})

窗口渲染进程

1
2
3
4
5
6
// 渲染进程
ipcRenderer.invoke('get-data').then((data) => {
console.log(data) // 获取到的数据
})

ipcRenderer.send('save-data', { name: 'John', age: 30 }) // 发送数据到主进程

禁用数据隔离

创建窗口时,lectron 提供了 webPreferences 中的 partition 和 session 选项来控制浏览器会话和数据隔离。如果你不进行特别的配置,默认情况下,所有窗口都会共享同一个 session
例如:

  • Cookies
  • IndexedDB索引数据库
  • localStorage 本地存储

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const { BrowserWindow } = require('electron')

// 创建主窗口 A
const mainWindow = new BrowserWindow({
webPreferences: {
partition: 'persist:shared', // 使用同一持久化 session 名称
}
})

// 创建另一个窗口 B,与窗口 A 共享相同的 session
const anotherWindow = new BrowserWindow({
webPreferences: {
partition: 'persist:shared', // 与窗口 A 使用相同的 partition 名称
}
})

渲染进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 窗口a保存数据到 localStorage
function saveData() {
const inputData = document.getElementById('inputData').value
localStorage.setItem('sharedData', inputData)
console.log('Data saved:', inputData)
}

// 打印 localStorage 中的数据
function printData() {
const storedData = localStorage.getItem('sharedData')
console.log('Stored Data:', storedData)
}

// 窗口b监听 storage 事件
window.addEventListener('storage', (event) => {
if (event.key === 'sharedData') {
console.log('Shared data updated:', event.newValue)
// 可选:更新页面显示的数据
}
})

partition 设置为 'persist:shared',确保 窗口 A 和 B 使用相同的会话存储。任何在 localStorageCookies 中的数据都将在这两个窗口之间共享。persist:shared 是持久会话,因此即使窗口关闭,数据也会被保留。

注意事项

  • 进程隔离:即使共享了 localStoragesession,不同窗口的 渲染进程还是相互隔离的,因此 JavaScript 变量或 Vuex/Pinia 的状态等不会共享。

  • 同步问题:即便是共享的 localStorage,其变更不会自动在多个窗口之间同步。如果一个窗口更新了 localStorage 数据,其他窗口不会自动得到通知。要实现数据的实时同步,仍然需要借助 IPC 通信 或 storage 事件监听。

  • 持久化会话名称:当 partition 前缀为 persist: 时,会话数据会持久化存储(即使应用关闭也会保留)。如果没有 persist:,则是临时的,窗口关闭后数据将清空。

示例:实现共享和实时更新
通过 partition 配置共享 localStorage 后,还可以监听 storage 事件来确保实时同步。例如:

1
2
3
4
5
// 在窗口 A 和 B 中的渲染进程
window.addEventListener('storage', (event) => {
console.log('Storage changed:', event.key, event.newValue)
// 根据 event.key 和 event.newValue 来更新 Pinia 或其他状态
})

总结

  • 配置相同的 partition 可以共享 localStorage 和 session。
  • 实时同步 仍然建议使用 storage 事件监听或 IPC。
  • 持久化和隔离控制:persist: 前缀确保数据在应用关闭后保留。