electron-vite相关

1. electron-vite相关的问题汇总

https://github.com/alex8088/electron-vite
https://cn.electron-vite.org/

2. electron-vite如何集成 Dockview

https://dockview.dev/templates/
完整 Dockview 浮动窗口示例 (Electron-Vite)

  1. ⚙️ 主进程 (Main Process) 设置
    在你的 Electron 主进程文件(通常是 src/main/index.ts 或 src/main/main.ts)中,你需要添加代码来监听渲染进程发出的创建新窗口的请求。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

TypeScript

// src/main/index.ts (或类似的主进程文件)
import { app, BrowserWindow, ipcMain } from 'electron';
import { join } from 'path';

// ... (省略了 createWindow 等初始化代码)

const externalWindows = new Map<string, BrowserWindow>();

// 监听渲染进程请求创建浮动面板窗口
ipcMain.on('create-external-panel-window', (_, data: { groupId: string, initialBounds: any, url: string }) => {
const { groupId, initialBounds, url } = data;

if (externalWindows.has(groupId)) {
externalWindows.get(groupId)?.focus();
return;
}

// 创建一个新的 Electron 浏览器窗口
const externalWindow = new BrowserWindow({
x: initialBounds.x,
y: initialBounds.y,
width: initialBounds.width,
height: initialBounds.height,
show: false,
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
// 注意:这里需要启用 sandbox,然后通过 preload 暴露安全 API
sandbox: true,
contextIsolation: true,
}
});

externalWindow.on('closed', () => {
externalWindows.delete(groupId);
// 通知主窗口面板已关闭
BrowserWindow.getAllWindows().forEach(win => {
if (win !== externalWindow) {
win.webContents.send('external-panel-closed', groupId);
}
});
});

// 加载 Dockview 外部窗口的特定 URL
// 我们将使用主窗口的 URL,但带上查询参数来指示它是外部窗口
externalWindow.loadURL(`${url}?groupId=${groupId}&external=true`);

externalWindow.once('ready-to-show', () => {
externalWindow.show();
});

externalWindows.set(groupId, externalWindow);
});

// 监听并转发来自主窗口的消息到特定的外部窗口
ipcMain.on('external-panel-message', (_, groupId: string, message: any) => {
const win = externalWindows.get(groupId);
if (win) {
win.webContents.send('dockview-message', message);
}
});
  1. Preload 脚本设置
    为了在渲染进程中安全地使用 IPC,你需要通过 contextBridge 在 Preload 脚本中暴露必要的 IPC 功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
TypeScript

// src/preload/index.ts (或类似的 Preload 文件)
import { contextBridge, ipcRenderer } from 'electron';

// ... (省略了其他 IPC 暴露)

contextBridge.exposeInMainWorld('electron', {
ipcRenderer: {
// Dockview 外部化功能所需的 IPC
send: (channel: string, ...args: any[]) => {
const validChannels = [
'create-external-panel-window',
'external-panel-message',
'dispose-external-panel-window'
];
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, ...args);
}
},
on: (channel: string, func: (...args: any[]) => void) => {
const validChannels = [
'dockview-message', // 接收来自主进程的消息
'external-panel-closed'
];
if (validChannels.includes(channel)) {
// ... (省略了类型检查和 IPC 监听的实现细节)
ipcRenderer.on(channel, (_event, ...args) => func(...args));
}
},
// ... 其他必要的 IPC 方法
}
});
  1. 🖼️ 渲染进程 (Renderer Process) - Dockview 组件修改
    你需要修改 DockviewContainer.vue,实现 createExternalWindow 钩子。

代码段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<template>
<div ref="containerRef" :class="{ 'dockview-external-host': isExternal }" style="height: 100vh; width: 100vw;"/>
</template>

<script setup lang="ts">
import { ref, onMounted, shallowRef } from 'vue';
import { createApp, type App } from 'vue';
import { DockviewComponent, IDockviewPanel, DockviewExternalWindow } from 'dockview';
import PanelContent from './PanelContent.vue';

// --- 检查是否为外部窗口 ---
// 简单方法:检查 URL 是否有 `?external=true` 查询参数
const isExternal = window.location.search.includes('external=true');
const urlParams = new URLSearchParams(window.location.search);
const groupId = urlParams.get('groupId') || '';

// ... (省略 PanelContent 挂载和 vueApps Map 的代码,见上一个回答)

// --- Dockview 外部化配置 ---
const createExternalWindow = (event: {
groupId: string;
bounds: { x: number; y: number; width: number; height: number };
}): DockviewExternalWindow => {

// 1. 通知主进程创建新窗口
window.electron.ipcRenderer.send('create-external-panel-window', {
groupId: event.groupId,
initialBounds: event.bounds,
// 传递当前 URL,让新窗口知道加载什么
url: window.location.href.split('?')[0]
});

// 2. 返回 Dockview 需要的 PostMessage 接口
return {
// 用于向新窗口发送数据 (由 Dockview 内部调用)
postMessage: (message) => {
window.electron.ipcRenderer.send('external-panel-message', event.groupId, message);
},
// 当浮动面板关闭或被重新拖回主窗口时调用
dispose: () => {
window.electron.ipcRenderer.send('dispose-external-panel-window', event.groupId);
}
};
};

// --- Dockview 实例化 ---
onMounted(() => {
if (!containerRef.value) return;

const dockview = new DockviewComponent({
parentElement: containerRef.value,
createComponent: createVueComponent,

// 启用外部拖拽
allowExternalDrag: true,

// 实现创建外部窗口的钩子
createExternalWindow: createExternalWindow,

// 如果是外部窗口,需要传入 groupdId 来恢复布局
is\'external\' = isExternal,
externalGroupId: groupId || undefined
});
dockviewRef.value = dockview;

// --- 外部窗口的特殊处理 ---
if (isExternal) {
// 1. 监听来自主窗口的消息,并转发给 Dockview 实例
window.electron.ipcRenderer.on('dockview-message', (message) => {
dockview.processExternalWindowMessage(message);
});
} else {
// 2. 主窗口初始化布局
dockview.addPanel({ id: 'panel-1', component: 'vue-panel', title: '主窗口面板 1' });
dockview.addPanel({ id: 'panel-2', component: 'vue-panel', title: '主窗口面板 2' });

// 3. 监听外部窗口关闭事件
window.electron.ipcRenderer.on('external-panel-closed', (closedGroupId) => {
// 通知 Dockview 外部组已关闭,以便它可以清理内部状态
dockview.removeExternalComponent(closedGroupId);
});
}

// ... (省略 resize 监听代码)
});
</script>

<style scoped>
/* 可选:为外部窗口提供一个不同的背景或边框 */
.dockview-external-host {
/* 外部窗口通常不需要边框 */
}
</style>

🔑 总结和操作步骤
设置 IPC (Preload & Main): 在主进程中配置 ipcMain 来创建 BrowserWindow,并在 Preload 中通过 contextBridge 安全暴露 ipcRenderer 的 send 和 on 方法。

实现 createExternalWindow: 在 DockviewContainer.vue 中,实现 createExternalWindow 钩子,它向主进程发送创建新窗口的请求,并返回 Dockview 所需的 postMessage 接口。

处理外部窗口初始化:

检查 URL 中的查询参数 (?external=true) 来判断当前窗口是主窗口还是浮动面板窗口。

如果是浮动面板窗口,在实例化 DockviewComponent 时传入 isExternal: true 和 externalGroupId,并监听主窗口发来的 dockview-message。

操作步骤:

运行你的 Electron-Vite 应用。

点击一个面板的标题栏。

将标题栏拖出主窗口的边界。

释放鼠标,一个新的、独立的 Electron 窗口就会弹出,其中包含你拖出的面板

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器