如何从 main.js 外包 Electron 菜单代码来分离 menu.js?

How outsource Electron menu code from main.js to separate menu.js?

我 main.js 中的 Electron 菜单代码现在真的很长。 是否可以将其外包给另一个文件? 如果可以,我还能从菜单中调用位于 main.js 中的功能吗?

// MENU BEGIN
/////////////////////////////////////////////////////////////////////
const { app, Menu } = require('electron')

const isMac = process.platform === 'darwin'
const isWin = process.platform === 'win32'

const template = [
    // { role: 'appMenu' }
    ...(isMac ? [{
        label: app.name,
        submenu: [
            { role: 'about' },
            { type: 'separator' },
            { role: 'services' },
            { type: 'separator' },
            { role: 'hide' },
            { role: 'hideOthers' },
            { role: 'unhide' },
            { type: 'separator' },
            { role: 'quit' }
        ]
    }] : []),
    // more menu code...
    {
        role: 'help',
        submenu: [
            {
                label: 'Learn More',
                click: async () => {
                    const { shell } = require('electron')
                    await shell.openExternal('https://github.com/aronsommer/eddy-g')
                }
            }
        ]
    }
]

const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
/////////////////////////////////////////////////////////////////////
// MENU END

随着 Electron 应用程序规模的增长,您必须开始将代码分离到它们自己的文件中。这就是所谓的分离你的关注点。它构成了一个整洁、易于阅读的 well-structured 项目。


将菜单的创建从 main.js 文件中分离到它自己的文件中(我们称之为 menu.js),然后导出它的 build() 功能。

menu.js(主线程)

// Import the necessary Electron modules
const electronApp = require('electron').app;
const electronMenu = require('electron').Menu;

const isMac = process.platform === 'darwin'
const isWin = process.platform === 'win32'

function build() {
    const template = [
        // { role: 'appMenu' }
        ...(isMac ? [{
            label: electronApp.name,
            submenu: [
                { role: 'about' },
                { type: 'separator' },
                { role: 'services' },
                { type: 'separator' },
                { role: 'hide' },
                { role: 'hideOthers' },
                { role: 'unhide' },
                { type: 'separator' },
                { role: 'quit' }
            ]
        }] : []),
        // more menu code...
        {
            role: 'help',
            submenu: [
                {
                    label: 'Learn More',
                    click: async () => {
                        const { shell } = require('electron')
                        await shell.openExternal('https://github.com/aronsommer/eddy-g')
                    }
                }
            ]
        }
    ];
    
    electronMenu.setApplicationMenu(electronMenu.buildFromTemplate(template));
}

// Export the publicly available function
module.exports = {build};

现在,在您的 main.js 文件中,像任何其他文件一样要求它,然后在应用程序 ready.

后调用 build() 函数

Note: I have called the menu build() function from within the createWindow() function as it is really part of window creation. IE: The menu is part of a window.

main.js(主线程)

const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;

const nodePath = require("path");

const appMenu = require('menu');

let window;

function createWindow() {
    const window = new electronBrowserWindow({
        x: 0,
        y: 0,
        width: 800,
        height: 600,
        show: false,
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            preload: nodePath.join(__dirname, 'preload.js')
        }
    });

    window.loadFile('index.html')
        .then(() => { appMenu.build(); } // Build the menu
        .then(() => { window.show(); });

    return window;
}

electronApp.on('ready', () => {
    window = createWindow();
});

electronApp.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        electronApp.quit();
    }
});

electronApp.on('activate', () => {
    if (electronBrowserWindow.getAllWindows().length === 0) {
        createWindow();
    }
});

关于您的问题“我还能从菜单中调用位于 main.js 中的功能吗?”。

答案是肯定的,如果您 require 那些函数位于 menu.js 文件的顶部。

如果您不想 require 您的 menu.js 文件中的所有这些功能,您可能需要 Node 的事件模块和菜单 clickemit一个事件并在你的其他脚本中监听它。这种设计允许非常模块化的应用程序,允许在以后毫无困难地添加新功能。


奖金

在单独的文件中构建菜单还允许您在需要更改任何菜单项时动态更新 (re-build) 菜单。

menu.js(主线程)

// Rebuild the menu on localisation change.
appEvents.on('locale:changed', () => {
    build();
});