代码之家  ›  专栏  ›  技术社区  ›  Brandon Mulas

如何在electronic+webpack中从Main进程获取javascript对象到Renderer进程

  •  1
  • Brandon Mulas  · 技术社区  · 10 月前

    我正在构建一个电子+webpack应用程序。我试图实现的结果是将“config.yaml”作为javascript对象加载到主进程中(我认为必须这样,因为渲染器无法访问节点的“fs”),然后使用IPC将其移动到我实际需要的渲染器中。文件结构如下:

    - .webpack/
    - node_modules/
    - src/
    +-- config.js
    +-- header.js
    +-- index.css
    +-- index.html
    +-- main.js
    +-- preload.js
    +-- renderer.js
    +-- theme.js
    - static/
    +-- cfg/
      +-- config.yaml
    +-- themes/
      +-- ...
    - .gitignore
    - forge.config.js
    - package-lock.json
    - package.json
    - webpack.main.config.js
    - webpack.renderer.config.js
    - webpack.rules.js
    

    main.js

    const { app, BrowserWindow, Menu, ipcMain } = require('electron');
    const fs = require('fs');
    const yaml = require('yaml');
    const theme = require('./theme.js');
    
    // Handle creating/removing shortcuts on Windows when installing/uninstalling.
    if (require('electron-squirrel-startup')) {
      app.quit();
    }
    
    let config = fs.readFileSync('./static/cfg/config.yaml', 'utf8');
    config = yaml.parse(config);
    
    const createWindow = () => {
      // Create the browser window.
      const mainWindow = new BrowserWindow({
        width: config.cl_win_width,
        height: config.cl_win_height,
        webPreferences: {
          preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
        },
      });
    
      // Remove the menu bar
      Menu.setApplicationMenu(null);
    
      // and load the index.html of the app.
      mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);
    
      // Open the DevTools.
      mainWindow.webContents.openDevTools();
    };
    
    // This method will be called when Electron has finished
    // initialization and is ready to create browser windows.
    // Some APIs can only be used after this event occurs.
    app.whenReady().then(() => {
    
      // Config
      ipcMain.handle('config', () => {
        return config;
      })
    
      // Theme API
      ipcMain.handle('get-themes', () => {
        const fileList = fs.readdirSync('./static/themes');
        let themeList = theme.getThemes(fileList);
        return themeList;
      })
    
      createWindow();
    
      // On OS X it's common to re-create a window in the app when the
      // dock icon is clicked and there are no other windows open.
      app.on('activate', () => {
        if (BrowserWindow.getAllWindows().length === 0) {
          createWindow();
        }
      });
    });
    
    // Quit when all windows are closed, except on macOS. There, it's common
    // for applications and their menu bar to stay active until the user quits
    // explicitly with Cmd + Q.
    app.on('window-all-closed', () => {
      if (process.platform !== 'darwin') {
        app.quit();
      }
    });
    

    preload.js

    // See the Electron documentation for details on how to use preload scripts:
    // https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts
    const { ipcRenderer, contextBridge } = require('electron');
    const header = require('./header.js');
    
    contextBridge.exposeInMainWorld('mainAPI', {
        getConfig: () => ipcRenderer.invoke('config'),
    });
    
    contextBridge.exposeInMainWorld('themeAPI', {
        listThemes: () => ipcRenderer.invoke('get-themes'),
    });
    
    contextBridge.exposeInMainWorld('headerAPI', header);
    

    renderer.js

    import './index.css';
    
    let headerAPI = window.headerAPI;
    
    // Load config
    let config;
    window.mainAPI.getConfig().then(res => {
        console.log(res);
        config = res;
    });
    
    // Test this
    console.log(config);
    
    const content = document.querySelector('#content');
    
    // Create Header
    headerAPI.createHeader(content);
    

    config.js

    export async function getConfig() {
        // Makes call to main process and returns the config object
        let config = await window.mainAPI.getConfig();
        console.log(config);
        return config
    }
    

    Webpack被配置为对我的静态文件使用文件加载器。

    当我用npm run、electronic forge运行这个应用程序时。执行后,webpack编译我的代码,并启动开发服务器。应用程序窗口加载。

    我希望看到开发工具控制台显示一条消息,其中包含注销的javascript对象。相反,我看到的是:

    undefined                        renderer.js:19
    
    {my javascript object}           renderer.js:14
    

    总的来说,控制台记录“config”对象表明它确实正确加载。

    在渲染器中,它正确地记录在第14行。我的理解是,因为我们在记录之前等待async函数返回,所以第14行应该在第19行之前执行。我考虑过的一个选项是“.then()”不会阻止脚本执行,这就是为什么会出现此错误。

    我的问题是:

    我如何将配置对象从main转移到渲染器,并在继续执行该脚本的其余部分之前等待其加载(注意,将有6000多行代码,所以我拒绝了将所有内容都放在.then()范围内的恶心想法)。

    只是一个想法:我以前在这个项目的上一次迭代中做过这个。我只是通过不使用main加载配置来管理它,而是使用config.js const fs = require('fs'); ,并定义了一个函数将其加载到那里,然后使用preload来公开它。这在这里不再有效,因为现在除了main.js之外,没有其他东西可以访问fs。我真的不知道该怎么办。

    如果有人能帮助我理解我在这里错过了什么,以及如何解决这个问题,我将不胜感激。谢谢!

    1 回复  |  直到 10 月前
        1
  •  1
  •   Robby Cornelissen    10 月前

    在下面的代码中,执行不会等待 Promise 返回从 window.mainAPI.getConfig() 在继续执行之前需要解决。传递给 then() 函数已执行 异步 这就是为什么你是第二名 console.log() 语句首先被调用。

    let config;
    window.mainAPI.getConfig().then(res => {
        console.log(res);
        config = res;
    });
    
    console.log(config);
    

    除了将所有其他代码放在 然后() 回拨。对于您的具体情况,最方便的方法是使用 top level await 语句(ES2022)等待 承诺 在继续执行之前需要解决。幸运的是,这是 available in Node.js ESM ,因此可用于电子:

    const config = await window.mainAPI.getConfig();
    console.log(config);