electron多窗口办理方案


李俊才 的 个人博客 (jcLee95):blog.csdn.net/qq_28550263…
邮箱 :291148484@163.com
本文地址:blog.csdn.net/qq_28550263…

仅发在以上地址发布,请勿转载!

简介: 单例形式进行全局状况办理的解决方案有许多。本文介绍一种围绕electron运用单个目标的署理并配套多个相关函数的窗口办理解决方案。


目 录


1. 概述

2. BrowserWindow 目标

  • 2.1 类型
  • 2.2 创立窗口

3. 经过 id 办理 BrowserWindow 目标

  • 3.1 目标概述
  • 3.2 窗口字典及其署理
  • 3.3 经过 id 办理窗口

    • 3.3.1 对窗口的 id 记载
    • 3.3.2 经过 id 索引窗口实例
    • 3.3.3 经过窗口实例操作窗口行为
  • 3.4 操作一切窗口
  • 3.5. 关于主窗口

4. 在烘托进程中操控窗口实例

  • 4.1 确认你要操控的窗口实例
  • 4.2 在烘托进程直接操作窗口
  • 4.3 增减进程一类操作

1. 概述

办理 electron 窗口即在主进程中办理由 BrowserWindow 等 electron API 创立的烘托进程。在工程化的大型 electron 项目中,可以单独创立进程办理的东西模块。本文分享一些窗口办理与操控上的经历。
一般来说,咱们是经过窗口 id 来仅有表示一个 electron 的,

2. BrowserWindow 目标

2.1 类型

该目标的类型为 Electron.CrossProcessExports.BrowserWindow,在 typescript 项目中为运用方便起见,你可以像下面这样为其起一个简短的别号:

// types.ts
export declare type EWindow = Electron.CrossProcessExports.BrowserWindow;

2.2 创立窗口

创立
目录:apps.mian
文件:index.ts

// index.ts
import { BrowserWindow } from "electron";
import { logger, WINDOW_PORT, RENDER_HTML_PATH } from "../../params";    // 全局装备的参数
import { mainWindowIDProxy, setWindowById } from "../../window_manager"; // 窗口办理东西
import { winparams } from "./params";                                    // 当时窗口参数
function newWinMain() {
  const window = new BrowserWindow(winparams as any);
  // 该函数用于记载该新建窗口的 ID ,在后面末节会讲解到
  const id = setWindowById(window);
  // 这条句子用于记载主窗口 ID ,在后面末节会提到
  mainWindowIDProxy.value = id.toString();
  // 一般来说:
  // - 在生产环境下加载的是打包构建好的 HTML 文件,需求指定当时烘托进程的 index.html 文件的路径
  // - 在开发环境下,往往会运用开发服务器,比方 vite 等都有供给,这时一般只需求指定本地端口
  window.loadURL(
    process.env.NODE_ENV === "production"
      ? `file://${RENDER_HTML_PATH.main}`
      : `http://127.0.0.1:${WINDOW_PORT.main}/`
  );
  logger.debug(
    `Start Main Window At URL: http://localhost:${WINDOW_PORT.main}/`
  );
  // 仅仅在非生产环境下,打开调试东西
  if (process.env.NODE_ENV !== "production") {
    window.webContents.openDevTools();
  }
  require("@electron/remote/main").enable(window.webContents);
  mainWindowIDProxy.value = id.toString()
  return id;
}
export { newWinMain };

文件:params.ts
该文件用于指定创立窗口所需求的参数,以下仅仅是示例,你可以自己根据需求指定参数。

// params.ts
import { APP_NAME } from "../../params";
export const winparams = {
    width: 1239,
    height: 766,
    resizable: true,
    minWidth: 966,
    minHeight: 696,
    transparent: false, 
    opacity: 1,
    hasShadow: true,
    movable: true, 
    minimizable: true,
    maximizable: true,
    closable: true,
    focusable: true,
    alwaysOnTop: false,
    fullscreen: false,
    fullscreenable: true,
    fullscreenWindowTitle: false,
    title: APP_NAME,
    icon: "../../../public/favicon_256x256.ico",
    show: true,
    paintWhenInitiallyHidden: true,
    frame: false,  // Specify `false` to create a frameless window. Default is `true`.
    parent: null,
    webPreferences: {
      devTools: true,
      nodeIntegration: true,
      nodeIntegrationInWorker: true,
      contextIsolation: false,
    }
  }

3. 经过 id 办理 BrowserWindow 目标

3.1 目标概述

办理 BrowserWindow 目标主要是在窗口创立后办理其显现、隐藏、最小化、最大话、复原、封闭等等行为。要办理窗口的行为就必先须仅有的指定所需求办理的窗口是谁——这是咱们主要的办理目标。

因而窗口一旦创立,咱们就需求记载窗口的 id,并且在一切其它需求运用的时候,在其它的模块中都可以获取到这些被记载的窗口 id。

3.2 窗口字典(线程池)及其署理

// window_manager.ts
const winDict:WindowDict = {};
import { logger } from "./params";
import { WindowDict, EWindow } from "./types";
const WindowDictProxy = new Proxy(winDict, {
  get: function(obj:WindowDict, prop:string){
    return obj[prop]
  },
  set: function(obj:WindowDict, prop:string, value:EWindow){
    if(obj[prop]){
      logger.error(`Window id '${prop}' has already existed.`)
      return false
    }else{
      obj[prop] = value;
      return true;
    }
  },
  has(target:WindowDict, key:string) {
    if(Object.getOwnPropertyNames(target).includes(key)){
      return true
    }else{
      return false
    }
  },
  ownKeys:function(target:WindowDict){
    return [...Object.getOwnPropertyNames(target)]
  },
  deleteProperty: function(target:WindowDict, prop:string){
    try{
      delete target[prop]
      return true;
    }catch(e){
      logger.warn(e);
      return false;
    }
  }
})

3.3 经过 id 办理窗口

3.3.1 对窗口的 id 记载

// window_manager.ts
/** 经过 id 托管窗口 */
function setWindowById(Window: EWindow){
  try{
    const id = Window.id.toString();
    WindowDictProxy[id] = Window;
    return id;
  }
  catch(e){
    logger.error(`Can not set Window By ID '${Window.id}', as the following reasons:\n${e}`)
    return ;
  }
}

3.3.2 经过 id 索引窗口实例

// window_manager.ts
/**经过ID索引窗口 */
function getWindowById(id:string):EWindow{
  return WindowDictProxy[id]
}

3.3.3 经过窗口实例操作窗口行为

// window_manager.ts
/**经过 ID 封闭窗口 */
function hideWindowById(id:string){
  try{
    getWindowById(id).hide()
    return true;
  }catch(e){
    logger.error(`Can not hide Window By ID '${id}', as the following reasons:\n${e}`)
    return false;
  }
}
/**经过 ID 显现窗口 */
function showWindowById(id:string){
  try{
    getWindowById(id).show()
    return true;
  }catch(e){
    logger.error(`Can not show Window By ID '${id}', as the following reasons:\n${e}`)
    return false;
  }
}
/**经过 ID 隐藏窗口 */
function closeWindowById(id:string){
  try{
    getWindowById(id).close();
    delete WindowDictProxy[id];
    return true;
  }catch(e){
    logger.error(`Can not close Window By ID '${id}', as the following reasons:\n${e}`)
    return false;
  }
}
/**经过 ID 最大化窗口 */
function maximizeWindowById(id:string){
  try{
    getWindowById(id).maximize();
    delete WindowDictProxy[id];
    return true;
  }catch(e){
    logger.error(`Can not maximize Window By ID '${id}', as the following reasons:\n${e}`)
    return false;
  }
}
/**经过 ID 最小化窗口 */
function minimizeWindowById(id:string){
  try{
    getWindowById(id).minimize();
    delete WindowDictProxy[id];
    return true;
  }catch(e){
    logger.error(`Can not minimize Window By ID '${id}', as the following reasons:\n${e}`)
    return false;
  }
}
/**经过 ID 复原窗口 */
function restoreWindowById(id:string){
  try{
    getWindowById(id).restore();
    delete WindowDictProxy[id];
    return true;
  }catch(e){
    logger.error(`Can not restore Window By ID '${id}', as the following reasons:\n${e}`)
    return false;
  }
}

3.4 操作一切窗口

// window_manager.ts
/**封闭一切窗口 */
function closeAllWindows(){
  Object.getOwnPropertyNames(WindowDictProxy).forEach(
    (id:string)=>{
      WindowDictProxy[id].close();
      delete WindowDictProxy[id];
    }
  )
}

3.5. 关于主窗口

主窗口是我最常运用的窗口,关于这样的窗口有时候往往会在创立其它窗口或许创立对话框(dialog)等场景用到,因而我倾向于将其 id 记载下来,运用时不经过 id 而直接获取会比较方便。这样完成:

// window_manager.ts
const mainWindowID:{value:undefined|string} = {value:undefined};
const mainWindowIDProxy = new Proxy(mainWindowID,{
  get: function(obj:{value:undefined|string}, prop:string){
    return obj.value
  },
  set: function(obj:{value:undefined|string}, prop:'value', value){
    obj['value'] = value
    return true;
  }
})

4. 在烘托进程中操控窗口实例

4.1 确认你要操控的窗口实例

需求在烘托进程中操控窗口的关键在于获取当时窗口实例。当时的 electron 版别中现已移除了 remote 模块,在不运用该模块的前提下,只要经过 ipc 通讯对模块进行操控,这意味着每个窗口对应的烘托进程在其从追进程中创立时就应该在主进程中记载这个窗口并仅有告知经过创立后在烘托进程中运用 ipcrender 对主进程进行问询的 id 值。

还有一种办法便是运用 remote 模块。尽管官方现已从 electron API 中删去了,但咱们仍然可以经过安装 @electron/remote 及逆行运用。

npm i -D @electron/remote

要运用该模块,首要你需求在 主进程 中经过以下句子启用它:

require("@electron/remote/main").initialize();

然后你可以运用以下办法获取当时窗口的实例:

// 返回当时窗口实例
getCurrentWindow(){
  return require('@electron/remote').getCurrentWindow();
},

4.2 在烘托进程直接操作窗口

在烘托进程中,你可以参阅以下办法进行运用:

// 烘托进程
// 窗口的最大化和复原的切换
windowToggle(){
  if(this.windowSizeIcon === "window-max"){
    this.getCurrentWindow().maximize();
    this.windowSizeIcon = "window-middle";
  }
  else{
    this.getCurrentWindow().restore();
    this.windowSizeIcon = "window-max";
  }
},
// 最小化窗口
windowMin(){
  this.getCurrentWindow().minimize()
},

4.3 增减进程一类操作

关于封闭窗口等触及增减进程一类的操作,会显得比较特别。关于从烘托进程封闭窗口,在烘托进程中,我是这样处理的:

// 烘托进程
const { ipcRenderer } = require('electron');
// 窗口封闭
windowClose(){
  const id = this.getCurrentWindow().id;
  ipcRenderer.send("ipc-window-manager-by-id",{
    action: 'close',
    id: id
  })
},

可以看出,我并没有直接在烘托进程中封闭窗口。这是为了咱们的进程能在主进程的 进程池 中进程统一办理,避免从烘托进程封闭窗口后,主进程的进程池中却还有该进程的记载。因而,在主进程中咱们需求监听该封闭音讯,并调用 closeWindowById 办法。

// 主进程
import { ipcMain } from "electron";
  ipcMain.on("ipc-window-manager-by-id", (event, arg) => {
    const id = arg.id;
    const action = arg.action;
    switch (action) {
      case "hide":
        hideWindowById(id);
        break;
      case "show":
        showWindowById(id);
        break;
      case "maximize":
        maximizeWindowById(id);
        break;
      case "minimize":
        minimizeWindowById(id);
        break;
      case "restore":
        restoreWindowById(id);
        break;
       // 从烘托进程封闭终究在主进程完成封闭
      case "close":     
        // 调用该办法,可以删去进程池中的记载
        closeWindowById(id);
        break;
    }
  });