@lynker-desktop/electron-window-manager 0.0.9-alpha.46 → 0.0.9-alpha.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/main/index.js CHANGED
@@ -81,6 +81,8 @@ class WindowsManager {
81
81
  * - 'localhost'、'127.0.0.1'、'::1' 以及局域网 IP(如 192.168.x.x、10.x.x.x、172.16.x.x~172.31.x.x)都视为本地白名单。
82
82
  */
83
83
  constructor(preload, loadingViewUrl, errorViewUrl, preloadWebContentsConfig, webviewDomainWhiteList) {
84
+ // 按名称索引的 Map,用于加速查找
85
+ this.windowsByName = new Map();
84
86
  // 预加载的窗口
85
87
  this.preloadedBW = null;
86
88
  // 预加载的窗口(无边框,有按钮)
@@ -91,9 +93,10 @@ class WindowsManager {
91
93
  this.preloadedBV = null;
92
94
  this.preloading = false;
93
95
  this.webviewDomainWhiteList = [];
94
- // 创建队列相关属性
95
- this.createQueue = [];
96
- this.isCreating = false;
96
+ // 窗口销毁检查的防抖函数,避免频繁检查
97
+ this.cleanupDestroyedWindowsDebounced = lodash.debounce(() => {
98
+ this._cleanupDestroyedWindows();
99
+ }, 500);
97
100
  /**
98
101
  * 防抖的排序方法
99
102
  * @param window 目标窗口
@@ -146,6 +149,20 @@ class WindowsManager {
146
149
  log('error', 'setPreloadWebContentsConfig error:', error);
147
150
  }
148
151
  }
152
+ /**
153
+ * Promise 超时包装函数
154
+ * @param promise 要包装的 Promise
155
+ * @param timeout 超时时间(毫秒)
156
+ * @param errorMessage 超时错误信息
157
+ */
158
+ async _withTimeout(promise, timeout, errorMessage) {
159
+ const timeoutPromise = new Promise((_, reject) => {
160
+ setTimeout(() => {
161
+ reject(new Error(errorMessage));
162
+ }, timeout);
163
+ });
164
+ return Promise.race([promise, timeoutPromise]);
165
+ }
149
166
  /**
150
167
  * 预加载实例
151
168
  */
@@ -156,54 +173,63 @@ class WindowsManager {
156
173
  try {
157
174
  if (this.preloadWebContentsConfig) {
158
175
  log('log', 'preloadWebContentsConfig: ', this.preloadWebContentsConfig);
176
+ const preloadPromises = [];
159
177
  // 根据配置决定是否预加载普通窗口
160
- if (this.preloadWebContentsConfig.enableBW !== false) {
161
- if (!this.preloadedBW) {
162
- this._createPreloadBW({}).then(i => {
163
- this.preloadedBW = i;
164
- log('log', 'init preloadedBW: ', !!this.preloadedBW);
165
- });
166
- }
178
+ if (this.preloadWebContentsConfig.enableBW !== false && !this.preloadedBW) {
179
+ preloadPromises.push(this._createPreloadBW({}).then(i => {
180
+ this.preloadedBW = i;
181
+ log('log', 'init preloadedBW: ', !!this.preloadedBW);
182
+ }).catch(error => {
183
+ log('error', '预加载 BW 失败:', error);
184
+ }));
167
185
  }
168
186
  // 根据配置决定是否预加载无边框有按钮的窗口
169
- if (this.preloadWebContentsConfig.enableBW_FramelessWithButtons !== false) {
170
- if (!this.preloadedBW_FramelessWithButtons) {
171
- this._createPreloadBW({
172
- frame: false,
173
- // transparent: true,
174
- autoHideMenuBar: true,
175
- titleBarStyle: 'hidden',
176
- }).then(i => {
177
- this.preloadedBW_FramelessWithButtons = i;
178
- log('log', 'init preloadedBW_FramelessWithButtons: ', !!this.preloadedBW_FramelessWithButtons);
179
- });
180
- }
187
+ if (this.preloadWebContentsConfig.enableBW_FramelessWithButtons !== false && !this.preloadedBW_FramelessWithButtons) {
188
+ preloadPromises.push(this._createPreloadBW({
189
+ frame: false,
190
+ autoHideMenuBar: true,
191
+ titleBarStyle: 'hidden',
192
+ }).then(i => {
193
+ this.preloadedBW_FramelessWithButtons = i;
194
+ log('log', 'init preloadedBW_FramelessWithButtons: ', !!this.preloadedBW_FramelessWithButtons);
195
+ }).catch(error => {
196
+ log('error', '预加载 BW_FramelessWithButtons 失败:', error);
197
+ }));
181
198
  }
182
199
  // 根据配置决定是否预加载无边框无按钮的窗口
183
- if (this.preloadWebContentsConfig.enableBW_FramelessNoButtons !== false) {
184
- if (!this.preloadedBW_FramelessNoButtons) {
185
- this._createPreloadBW({
186
- frame: false,
187
- // transparent: true,
188
- autoHideMenuBar: true,
189
- titleBarStyle: 'default',
190
- }).then(i => {
191
- this.preloadedBW_FramelessNoButtons = i;
192
- log('log', 'init preloadedBW_FramelessNoButtons: ', !!this.preloadedBW_FramelessNoButtons);
193
- });
194
- }
200
+ if (this.preloadWebContentsConfig.enableBW_FramelessNoButtons !== false && !this.preloadedBW_FramelessNoButtons) {
201
+ preloadPromises.push(this._createPreloadBW({
202
+ frame: false,
203
+ autoHideMenuBar: true,
204
+ titleBarStyle: 'default',
205
+ }).then(i => {
206
+ this.preloadedBW_FramelessNoButtons = i;
207
+ log('log', 'init preloadedBW_FramelessNoButtons: ', !!this.preloadedBW_FramelessNoButtons);
208
+ }).catch(error => {
209
+ log('error', '预加载 BW_FramelessNoButtons 失败:', error);
210
+ }));
195
211
  }
196
212
  // 根据配置决定是否预加载浏览器视图
197
- if (this.preloadWebContentsConfig.enableBV !== false) {
198
- if (!this.preloadedBV) {
199
- this._createPreloadBV().then(i => {
200
- this.preloadedBV = i;
201
- log('log', 'init preloadedBV: ', !!this.preloadedBV);
202
- });
213
+ if (this.preloadWebContentsConfig.enableBV !== false && !this.preloadedBV) {
214
+ preloadPromises.push(this._createPreloadBV().then(i => {
215
+ this.preloadedBV = i;
216
+ log('log', 'init preloadedBV: ', !!this.preloadedBV);
217
+ }).catch(error => {
218
+ log('error', '预加载 BV 失败:', error);
219
+ }));
220
+ }
221
+ if (preloadPromises.length > 0) {
222
+ // 添加超时机制,默认 10 秒超时
223
+ const timeout = 10000; // 10 秒
224
+ try {
225
+ await this._withTimeout(Promise.allSettled(preloadPromises), timeout, `预加载超时(${timeout}ms)`);
226
+ }
227
+ catch (error) {
228
+ log('error', '预加载超时:', error);
229
+ // 超时后继续执行,不阻塞后续流程
203
230
  }
204
231
  }
205
232
  }
206
- await new Promise(resolve => setTimeout(resolve, 1000));
207
233
  }
208
234
  catch (e) {
209
235
  log('error', '预创建实例失败', e);
@@ -630,46 +656,45 @@ class WindowsManager {
630
656
  }
631
657
  window.webContents.on('did-attach-webview', (_event, webContents) => {
632
658
  const tryEnable = () => {
633
- const url = webContents.getURL();
634
- // 判断是否本地/内网IP
635
- const isLocalhost = (hostname) => {
636
- return (hostname === 'localhost' ||
637
- hostname === '127.0.0.1' ||
638
- hostname === '::1' ||
639
- /^192\.168\./.test(hostname) ||
640
- /^10\./.test(hostname) ||
641
- /^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(hostname));
642
- };
643
- if (this.webviewDomainWhiteList && this.webviewDomainWhiteList.length > 0) {
644
- try {
645
- const { hostname } = new URL(url);
646
- // 优化白名单判断,支持 .example.com 形式的子域名通配和本地/内网IP
647
- const isWhiteListed = this.webviewDomainWhiteList.some(domain => {
648
- if (domain === 'localhost' || domain === '127.0.0.1' || domain === '::1') {
649
- return isLocalhost(hostname);
650
- }
651
- if (domain.startsWith('.')) {
652
- // .example.com 允许所有 *.example.com
653
- return hostname === domain.slice(1) || hostname.endsWith(domain);
659
+ try {
660
+ const url = webContents.getURL();
661
+ if (!url || url === 'about:blank') {
662
+ return;
663
+ }
664
+ if (this.webviewDomainWhiteList && this.webviewDomainWhiteList.length > 0) {
665
+ try {
666
+ const { hostname } = new URL(url);
667
+ // 优化白名单判断,支持 .example.com 形式的子域名通配和本地/内网IP
668
+ const isWhiteListed = this.webviewDomainWhiteList.some(domain => {
669
+ if (domain === 'localhost' || domain === '127.0.0.1' || domain === '::1') {
670
+ return this._isLocalhost(hostname);
671
+ }
672
+ if (domain.startsWith('.')) {
673
+ // .example.com 允许所有 *.example.com
674
+ return hostname === domain.slice(1) || hostname.endsWith(domain);
675
+ }
676
+ else {
677
+ // 精确匹配
678
+ return hostname === domain;
679
+ }
680
+ }) || this._isLocalhost(hostname); // 允许本地回环和内网地址
681
+ if (isWhiteListed) {
682
+ enable(webContents);
654
683
  }
655
684
  else {
656
- // 精确匹配
657
- return hostname === domain;
685
+ log('log', 'webview 域名未在白名单,未启用 remote', url);
658
686
  }
659
- }) || isLocalhost(hostname); // 允许本地回环和内网地址
660
- if (isWhiteListed) {
661
- enable(webContents);
662
687
  }
663
- else {
664
- log('log', 'webview 域名未在白名单,未启用 remote', url);
688
+ catch {
689
+ log('log', 'webview url 解析失败,未启用 remote', url);
665
690
  }
666
691
  }
667
- catch {
668
- log('log', 'webview url 解析失败,未启用 remote', url);
692
+ else {
693
+ enable(webContents); // 没有配置白名单则全部允许
669
694
  }
670
695
  }
671
- else {
672
- enable(webContents); // 没有配置白名单则全部允许
696
+ catch (error) {
697
+ log('error', 'tryEnable webview error:', error);
673
698
  }
674
699
  };
675
700
  // 只监听一次,防止多次触发
@@ -681,11 +706,14 @@ class WindowsManager {
681
706
  webContents.on('did-navigate', onDidNavigate);
682
707
  webContents.on('did-finish-load', onDidNavigate);
683
708
  });
684
- window.webContents.on('close', () => {
685
- this.windows.delete(window.id || window._id);
686
- });
709
+ // window.webContents.on('close', () => {
710
+ // this.windows.delete(window.id || window._id)
711
+ // })
687
712
  window.webContents.on('destroyed', () => {
688
- this.windows.delete(window.id || window._id);
713
+ const winId = window.id || window._id;
714
+ this.windows.delete(winId);
715
+ // 同步清理名称索引
716
+ this.windowsByName.delete(window._name);
689
717
  });
690
718
  window.webContents.on('dom-ready', () => {
691
719
  if (openDevTools) {
@@ -700,14 +728,20 @@ class WindowsManager {
700
728
  // @ts-ignore
701
729
  window.on('closed', () => {
702
730
  log('log', 'closed', window.id, window._name);
703
- this.windows.delete(window.id || window._id);
731
+ const winId = window.id || window._id;
732
+ this.windows.delete(winId);
733
+ // 同步清理名称索引
734
+ this.windowsByName.delete(window._name);
704
735
  });
705
736
  }
706
737
  if (type === 'BV') {
707
738
  parentWin?.addBrowserView(window);
708
739
  log('log', 'create - addBrowserView');
709
740
  }
710
- this.windows.set(window.id || window._id || window.webContents.id, window);
741
+ const winId = window.id || window._id || window.webContents.id;
742
+ this.windows.set(winId, window);
743
+ // 同步更新名称索引
744
+ this.windowsByName.set(window._name, winId);
711
745
  log('log', 'create', this.windows.keys());
712
746
  // 初始化值
713
747
  window.webContents.on('did-finish-load', () => {
@@ -901,149 +935,213 @@ class WindowsManager {
901
935
  window.webContents.on('did-stop-loading', onFailure);
902
936
  }
903
937
  }
938
+ /**
939
+ * 检查窗口是否已销毁
940
+ */
941
+ _isWindowDestroyed(win) {
942
+ try {
943
+ return !win || !win.webContents || win.webContents.isDestroyed();
944
+ }
945
+ catch {
946
+ return true;
947
+ }
948
+ }
949
+ /**
950
+ * 判断是否本地/内网IP
951
+ */
952
+ _isLocalhost(hostname) {
953
+ return (hostname === 'localhost' ||
954
+ hostname === '127.0.0.1' ||
955
+ hostname === '::1' ||
956
+ /^192\.168\./.test(hostname) ||
957
+ /^10\./.test(hostname) ||
958
+ /^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(hostname));
959
+ }
960
+ /**
961
+ * 清理已销毁的窗口(延迟执行,避免频繁检查)
962
+ */
963
+ _cleanupDestroyedWindows() {
964
+ const toDelete = [];
965
+ this.windows.forEach((win, key) => {
966
+ if (this._isWindowDestroyed(win)) {
967
+ toDelete.push(key);
968
+ // 同步清理名称索引
969
+ if (win?._name) {
970
+ this.windowsByName.delete(win._name);
971
+ }
972
+ }
973
+ });
974
+ toDelete.forEach(key => this.windows.delete(key));
975
+ }
904
976
  get(idOrName) {
905
977
  log('log', 'get', idOrName);
906
978
  let win;
907
- this.windows.forEach((i, key) => {
908
- try {
909
- if (!(i && i?.webContents?.isDestroyed && !i?.webContents?.isDestroyed?.())) {
910
- this.windows.delete(key);
911
- }
912
- }
913
- catch (error) {
914
- log('error', 'get');
979
+ if (typeof idOrName === 'number') {
980
+ // 按 ID 查找(O(1))
981
+ win = this.windows.get(idOrName);
982
+ // 如果找不到,尝试按 _id 查找
983
+ if (!win) {
984
+ this.windows.forEach(w => {
985
+ if (w._id === idOrName) {
986
+ win = w;
987
+ }
988
+ });
915
989
  }
916
- if (typeof idOrName === 'number') {
917
- if (i?.id === idOrName || i?._id === idOrName) {
918
- win = i;
919
- }
990
+ }
991
+ else if (typeof idOrName === 'string') {
992
+ // 按名称查找(O(1),使用索引)
993
+ const winId = this.windowsByName.get(idOrName);
994
+ if (winId !== undefined) {
995
+ win = this.windows.get(winId);
920
996
  }
921
- else if (typeof idOrName === 'string') {
922
- if (i?._name === idOrName) {
923
- win = i;
924
- }
997
+ // 如果索引中找不到,回退到遍历查找(兼容旧数据)
998
+ if (!win) {
999
+ this.windows.forEach(w => {
1000
+ if (w._name === idOrName) {
1001
+ win = w;
1002
+ // 更新索引
1003
+ this.windowsByName.set(idOrName, w.id || w._id);
1004
+ }
1005
+ });
925
1006
  }
926
- });
927
- // @ts-ignore
928
- if (win && win?.webContents?.isDestroyed && !win?.webContents?.isDestroyed?.()) {
1007
+ }
1008
+ // 检查找到的窗口是否已销毁
1009
+ if (win && !this._isWindowDestroyed(win)) {
929
1010
  return win;
930
1011
  }
1012
+ // 窗口已销毁,触发清理
1013
+ if (win) {
1014
+ const winId = win.id || win._id;
1015
+ this.windows.delete(winId);
1016
+ if (win._name) {
1017
+ this.windowsByName.delete(win._name);
1018
+ }
1019
+ }
1020
+ // 延迟清理其他已销毁的窗口
1021
+ this.cleanupDestroyedWindowsDebounced();
931
1022
  return undefined;
932
1023
  }
933
1024
  getAll(type) {
934
1025
  log('log', 'getAll');
935
- const bwWindows = new Map();
936
- const bvWindows = new Map();
1026
+ // 先清理已销毁的窗口
1027
+ const toDelete = [];
937
1028
  this.windows.forEach((win, key) => {
938
- if (!(win && win?.webContents?.isDestroyed && !win?.webContents?.isDestroyed?.())) {
939
- this.windows.delete(key);
940
- }
941
- if (win?._type === 'BW') {
942
- bwWindows.set(key, win);
943
- }
944
- if (win?._type === 'BV') {
945
- bvWindows.set(key, win);
1029
+ if (this._isWindowDestroyed(win)) {
1030
+ toDelete.push(key);
1031
+ if (win?._name) {
1032
+ this.windowsByName.delete(win._name);
1033
+ }
946
1034
  }
947
1035
  });
948
- if (type === 'BW') {
949
- return bwWindows;
1036
+ toDelete.forEach(key => this.windows.delete(key));
1037
+ if (!type) {
1038
+ return this.windows;
950
1039
  }
951
- if (type === 'BV') {
952
- return bvWindows;
953
- }
954
- return this.windows;
1040
+ const result = new Map();
1041
+ this.windows.forEach((win, key) => {
1042
+ if (!this._isWindowDestroyed(win) && win._type === type) {
1043
+ result.set(key, win);
1044
+ }
1045
+ });
1046
+ // 使用类型断言,TypeScript 会通过方法重载确保类型正确
1047
+ return result;
955
1048
  }
956
1049
  close(idOrName) {
957
1050
  log('log', 'close', idOrName);
958
- let win = undefined;
959
- this.windows.forEach((i) => {
960
- if (typeof idOrName === 'number') {
961
- if (i?.id === idOrName || i?._id === idOrName) {
962
- win = i;
963
- }
964
- }
965
- else if (typeof idOrName === 'string') {
966
- if (i?._name === idOrName) {
967
- win = i;
968
- }
969
- }
970
- });
971
- // @ts-ignore
972
- win && this.windows.delete(win?.id);
973
- // @ts-ignore
974
- if (win) {
975
- // @ts-ignore
1051
+ const win = this.get(idOrName);
1052
+ if (!win) {
1053
+ return false;
1054
+ }
1055
+ const winId = win.id || win._id;
1056
+ this.windows.delete(winId);
1057
+ if (win._name) {
1058
+ this.windowsByName.delete(win._name);
1059
+ }
1060
+ try {
976
1061
  if (win._type === 'BV') {
1062
+ // 从所有 BW 窗口中移除该 BV
977
1063
  this.windows.forEach(i => {
978
- if (i?._type === 'BW') {
1064
+ if (i?._type === 'BW' && !this._isWindowDestroyed(i)) {
979
1065
  const _win = i;
980
- _win.removeBrowserView(win);
1066
+ try {
1067
+ _win.removeBrowserView(win);
1068
+ }
1069
+ catch (error) {
1070
+ // 忽略错误,可能已经移除
1071
+ }
981
1072
  }
982
1073
  });
983
1074
  }
984
1075
  // @ts-ignore
985
- win?.webContents?.destroy?.();
1076
+ win.webContents?.destroy?.();
986
1077
  // @ts-ignore
987
- win?.close?.();
1078
+ win.close?.();
988
1079
  // @ts-ignore
989
- win?.destroy?.();
1080
+ win.destroy?.();
1081
+ }
1082
+ catch (error) {
1083
+ log('error', 'close window error:', error);
990
1084
  }
991
1085
  return true;
992
1086
  }
993
1087
  closeAll() {
994
1088
  log('log', 'closeAll');
995
- this.windows.forEach((win) => {
1089
+ const windowsToClose = Array.from(this.windows.values());
1090
+ // 先清空 Map,避免在遍历过程中修改
1091
+ this.windows.clear();
1092
+ this.windowsByName.clear();
1093
+ // 收集所有 BW 窗口,用于批量移除 BV
1094
+ const bwWindows = windowsToClose.filter(w => w._type === 'BW' && !this._isWindowDestroyed(w));
1095
+ const bvWindows = windowsToClose.filter(w => w._type === 'BV');
1096
+ // 先从所有 BW 窗口中移除 BV
1097
+ bvWindows.forEach(bv => {
1098
+ bwWindows.forEach(bw => {
1099
+ try {
1100
+ bw.removeBrowserView(bv);
1101
+ }
1102
+ catch (error) {
1103
+ // 忽略错误,可能已经移除
1104
+ }
1105
+ });
1106
+ });
1107
+ // 然后销毁所有窗口
1108
+ windowsToClose.forEach((win) => {
996
1109
  try {
997
- win && this.windows.delete(win?.id);
998
- // @ts-ignore
999
- if (win._type === 'BV') {
1000
- this.windows.forEach(i => {
1001
- if (i?._type === 'BW') {
1002
- const _win = i;
1003
- _win.removeBrowserView(win);
1004
- }
1005
- });
1110
+ if (this._isWindowDestroyed(win)) {
1111
+ return;
1006
1112
  }
1007
1113
  // @ts-ignore
1008
- win?.webContents?.destroy?.();
1114
+ win.webContents?.destroy?.();
1009
1115
  // @ts-ignore
1010
- win?.close?.();
1116
+ win.close?.();
1011
1117
  // @ts-ignore
1012
- win?.destroy?.();
1118
+ win.destroy?.();
1013
1119
  }
1014
1120
  catch (error) {
1121
+ log('error', 'closeAll error:', error);
1015
1122
  }
1016
1123
  });
1017
1124
  }
1018
1125
  rename(idOrName, newName) {
1019
1126
  log('log', 'rename', idOrName, newName);
1020
- let win = undefined;
1021
- // 先查找目标窗口
1022
- if (typeof idOrName === 'number') {
1023
- win = this.get(idOrName);
1024
- }
1025
- else if (typeof idOrName === 'string') {
1026
- this.windows.forEach(i => {
1027
- if (i?._name === idOrName) {
1028
- win = i;
1029
- }
1030
- });
1031
- }
1127
+ const win = this.get(idOrName);
1032
1128
  if (!win) {
1033
- // 没有找到目标窗口
1034
1129
  return undefined;
1035
1130
  }
1036
- // 检查新名字是否已存在
1037
- let nameExists = false;
1038
- this.windows.forEach(i => {
1039
- if (i !== win && i?._name === newName) {
1040
- nameExists = true;
1131
+ // 检查新名字是否已存在(使用索引加速)
1132
+ const existingWinId = this.windowsByName.get(newName);
1133
+ if (existingWinId !== undefined) {
1134
+ const existingWin = this.windows.get(existingWinId);
1135
+ if (existingWin && existingWin !== win && !this._isWindowDestroyed(existingWin)) {
1136
+ // 新名字已被占用
1137
+ return undefined;
1041
1138
  }
1042
- });
1043
- if (nameExists) {
1044
- // 新名字已被占用
1045
- return undefined;
1046
1139
  }
1140
+ // 更新名称索引
1141
+ const oldName = win._name;
1142
+ const winId = win.id || win._id;
1143
+ this.windowsByName.delete(oldName);
1144
+ this.windowsByName.set(newName, winId);
1047
1145
  // 修改名字并同步 webContents
1048
1146
  win._name = newName;
1049
1147
  initWebContentsVal(win, `${this.preload || ''}`);
@@ -1051,12 +1149,7 @@ class WindowsManager {
1051
1149
  }
1052
1150
  reInitUrl(idOrName, url) {
1053
1151
  log('log', 'reInitUrl', idOrName, url);
1054
- let win = undefined;
1055
- this.windows.forEach(i => {
1056
- if (i?._name === idOrName) {
1057
- win = i;
1058
- }
1059
- });
1152
+ const win = this.get(idOrName);
1060
1153
  if (!win) {
1061
1154
  return undefined;
1062
1155
  }
@@ -1257,21 +1350,25 @@ class WindowsManager {
1257
1350
  */
1258
1351
  _sortBrowserViews(window, addView) {
1259
1352
  try {
1353
+ if (window.isDestroyed()) {
1354
+ return;
1355
+ }
1260
1356
  const views = window.getBrowserViews() || [];
1261
1357
  if (views.length <= 1)
1262
1358
  return;
1263
1359
  log('log', 'sortBrowserViews', views?.map(i => i.webContents.id));
1264
- // 按zIndex层级排序,数值小的在前
1265
- const sortedViews = views.sort((a, b) => {
1360
+ // 创建排序后的视图数组(不修改原数组)
1361
+ const sortedViews = [...views].sort((a, b) => {
1266
1362
  const zIndexA = a._zIndex ?? 0;
1267
1363
  const zIndexB = b._zIndex ?? 0;
1268
1364
  return zIndexA - zIndexB;
1269
1365
  });
1270
- // 检查是否已经按正确顺序排列
1366
+ // 检查是否已经按正确顺序排列(优化:提前退出)
1271
1367
  let needsReorder = false;
1272
1368
  for (let i = 0; i < views.length; i++) {
1273
- // @ts-ignore
1274
- if (views[i].webContents.id !== sortedViews[i].webContents.id) {
1369
+ const view = views[i];
1370
+ const sortedView = sortedViews[i];
1371
+ if (!view || !sortedView || view.webContents.id !== sortedView.webContents.id) {
1275
1372
  needsReorder = true;
1276
1373
  break;
1277
1374
  }
@@ -1280,36 +1377,36 @@ class WindowsManager {
1280
1377
  // 如果已经按正确顺序排列,则不需要重新排序
1281
1378
  if (!needsReorder)
1282
1379
  return;
1283
- // 使用批量操作来减少闪烁
1284
- // 临时隐藏窗口内容
1285
- const wasVisible = window.isVisible();
1286
- if (wasVisible) {
1287
- // window.hide();
1288
- }
1289
- // 移除所有BrowserView
1380
+ // 移除所有BrowserView(排除刚添加的视图,避免不必要的操作)
1290
1381
  views.forEach(view => {
1291
1382
  if (addView?.webContents?.id !== view.webContents.id) {
1292
- // @ts-ignore
1293
- window.removeBrowserView(view, false);
1383
+ try {
1384
+ // @ts-ignore
1385
+ window.removeBrowserView(view, false);
1386
+ }
1387
+ catch (error) {
1388
+ // 忽略错误,视图可能已经被移除
1389
+ }
1294
1390
  }
1295
1391
  });
1296
1392
  // 按正确顺序重新添加
1297
1393
  sortedViews.forEach((view, index) => {
1298
- if (index === 0) {
1299
- // 第一个设置为当前视图
1300
- // @ts-ignore
1301
- window.setBrowserView(view, false);
1394
+ try {
1395
+ if (index === 0) {
1396
+ // 第一个设置为当前视图
1397
+ // @ts-ignore
1398
+ window.setBrowserView(view, false);
1399
+ }
1400
+ else {
1401
+ // 其他视图添加到后面
1402
+ // @ts-ignore
1403
+ window.addBrowserView(view, false);
1404
+ }
1302
1405
  }
1303
- else {
1304
- // 其他视图添加到后面
1305
- // @ts-ignore
1306
- window.addBrowserView(view, false);
1406
+ catch (error) {
1407
+ log('error', 'addBrowserView in sortBrowserViews error:', error);
1307
1408
  }
1308
1409
  });
1309
- // 恢复窗口显示
1310
- if (wasVisible) {
1311
- // window.show();
1312
- }
1313
1410
  }
1314
1411
  catch (error) {
1315
1412
  log('error', 'sortBrowserViews error:', error);