我试图制作一个应用程序,通过OS-utils获取CPU的使用情况,并在屏幕上更新它,但是从API到index.html,我什么都没有得到。只要我将os.cpuUsage保存在renderer.js文档中,应用程序就不会出现错误,但是我无法在index.html中得到任何更新
版本:电子: 17.1.2 os-utils: 0.0.14
// index.js
const { app, BrowserWindow, Menu, Tray, ipcMain } = require("electron");
const os = require("os-utils");
const path = require("path");
const createWindow = () => {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 1000,
    height: 600,
    frame: true,
    autoHideMenuBar: true,
    icon: __dirname + "/icon.ico",
    webPreferences: {
      preload: path.join(__dirname, "preload.js"),
    },
  });
};在这里,我将三个ipcRenderer.send()分配给cpu键,将其发送到index.js中由ipcMain.on()获取。
// preload.js
const { contextBridge, ipcRenderer } = require("electron");
const os = require("os-utils");
// sending cpu, mem and total-mem from cpuUsage through api channel
const API = {
  cpu: () => {
    os.cpuUsage(function (v) {
      ipcRenderer.send("cpu", v * 100);
      ipcRenderer.send("mem", os.freememPercentage() * 100);
      ipcRenderer.send("total-mem", os.totalmem() / 1024);
    });
  },
};
contextBridge.exposeInMainWorld("api", API);我尝试从通道cpu mem & total-mem获取返回,并使用新的stat值更新index.html,然后最后每秒钟调用cpu键函数来更新preload.js中的值,然后由index.js更新为index.html
// renderer.js
const { app } = require("electron");
app.whenReady().then(() => {
  ipcMain.on("cpu", (event, data) => {
    document.getElementById("cpu").innerHTML = data.toFixed(2);
  });
  ipcMain.on("mem", (event, data) => {
    document.getElementById("mem").innerHTML = data.toFixed(2);
  });
  ipcMain.on("total-mem", (event, data) => {
    document.getElementById("total-mem").innerHTML = data.toFixed(2);
  });
  setInterval(() => {
    window.api.cpu();
  }, 1000);
});<!-- index.html -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>CPU Stats Monitor</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <div class="box">
      <span class="label">CPU (%)</span>
      <span id="cpu">-</span>
    </div>
    <div class="box">
      <span class="label">Free Mem (%)</span>
      <span id="mem">-</span>
    </div>
    <div class="box">
      <span class="label">Total Mem (GB)</span>
      <span id="total-mem">-</span>
    </div>
    <script src="./renderer.js"></script>
  </body>
</html>我希望能够获取os.cpuUsage数据,并通过ipcRenderer将其发送给index.html,由renderer.js管理和更新,但是preload.js没有任何结果。甚至不是console.log(v)。所以我想要么cpu键根本不被调用。我可以从.
  setInterval(() => {
    window.api.cpu();
  }, 1000);只要它位于index.js内,但在那里,就不再定义window。
TLDR的故障排除;我只能调用未定义窗口的window.api.cpu()。
发布于 2022-03-26 16:21:42
我看到的一个常见问题是,人们试图实现一个工作的preload.js脚本,他们试图在其中实现具体的功能。我更喜欢采用只使用preload.js脚本的方法来白名单通道名称,这些名称可以在主线程和呈现线程之间进行通信。通过将您的关注点分离成不同的文件,它不仅大大简化了preload.js脚本的设计、配置和可读性,而且简化了代码的其余部分。
我认为将CPU统计数据的生成转移到主线程中是谨慎的。然后,这将从呈现线程窗口中取出负载,用于其他任务,并将任何常规轮询/繁重提升功能放入主线程。
在您的index.js文件中:
cpuStats。setInterval调用cpuStats函数并通过IPC将结果发送到window。index.js (主线程)
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const nodePath = require("path");
const os = require('os-utils');
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(() => { window.show(); });
    return window;
}
function cpuStats() {
    return new Promise((resolve) => {
        os.cpuUsage((value) => {
            let data = {
                'cpu': (value * 100).toFixed(2),
                'mem': (os.freememPercentage() * 100).toFixed(2),
                'totalMem': (os.totalmem() / 1024).toFixed(2)
            }
            resolve(data);
        })
    })
}
electronApp.on('ready', () => {
    window = createWindow();
    setInterval(() => {
        cpuStats()
            .then((data) => {
                window.webContents.send('cpuStats:update', data);
                console.log(data) // Testing
            })
    }, 1000);
});
electronApp.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        electronApp.quit();
    }
});
electronApp.on('activate', () => {
    if (electronBrowserWindow.getAllWindows().length === 0) {
        createWindow();
    }
});在您的index.html文件中,侦听来自主线程的IPC消息,并相应地更新DOM。
PS:为了简单起见,我将Javascript添加到文档底部的
<script>标记中。
index.html (渲染线程)
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>CPU Stats Monitor</title>
    </head>
    <body>
        <div class="box">
            <span class="label">CPU (%)</span>
            <span id="cpu">-</span>
        </div>
        <div class="box">
            <span class="label">Free Mem (%)</span>
            <span id="mem">-</span>
        </div>
        <div class="box">
            <span class="label">Total Mem (GB)</span>
            <span id="total-mem">-</span>
        </div>
    </body>
    <script>
        let cpu = document.getElementById('cpu');
        let mem = document.getElementById('mem');
        let totalMem = document.getElementById('total-mem');
        window.ipcRender.receive('cpuStats:update', (data) => {
            cpu.innerText = data.cpu;
            mem.innerText = data.mem;
            totalMem.innerText = data.totalMem;
            console.log(data); // Testing
        })
    </script>
</html>最后,preload.js脚本的一个简单的“仅通信”用例。
preload.js (主线程)
// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// White-listed channels.
const ipc = {
    'render': {
        // From render to main.
        'send': [],
        // From main to render.
        'receive': [
            'cpuStats:update'
        ],
        // From render to main and back again.
        'sendReceive': []
    }
};
// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
    // Allowed 'ipcRenderer' methods.
    'ipcRender', {
        // From render to main.
        send: (channel, args) => {
            let validChannels = ipc.render.send;
            if (validChannels.includes(channel)) {
                ipcRenderer.send(channel, args);
            }
        },
        // From main to render.
        receive: (channel, listener) => {
            let validChannels = ipc.render.receive;
            if (validChannels.includes(channel)) {
                // Deliberately strip event as it includes `sender`.
                ipcRenderer.on(channel, (event, ...args) => listener(...args));
            }
        },
        // From render to main and back again.
        invoke: (channel, args) => {
            let validChannels = ipc.render.sendReceive;
            if (validChannels.includes(channel)) {
                return ipcRenderer.invoke(channel, args);
            }
        }
    }
);理解电子的上下文隔离,进程间通信和渲染的过程contextBridge是必不可少的。
预加载的apiKey(s)公开给呈现线程中的window对象。
PS:您应该已经习惯于在普通html / Javascript编程中使用
window对象。例:let cpu = window.getElementById('cpu');。
在这个实例中,公开给window对象的window包括ipcRender.send、ipcRender.receive和ipcRender.invoke。因此,要在呈现线程中使用/访问这些apiKey函数,您可以分别调用window.ipcRender.send(...)、window.ipcRender.receive(...)和window.ipcRender.invoke(...)。
例如,现在让我们仔细研究一下ipcRender.send功能。
// Allowed 'ipcRenderer' methods.
'ipcRender', {
    // From render to main.
    send: (channel, args) => {
        let validChannels = ipc.render.send;
        if (validChannels.includes(channel)) {
            ipcRenderer.send(channel, args);
        }
    },
    ...
}这个window.ipcRender.send函数的函数参数(在呈现线程中使用)是channel和args。
channel是您希望指定的任何(字符串)名称,以标识您的“通道”。如果您熟悉Node的emitter.emit(eventName[,args]),请将"channel“名称看作相当于"eventName”的名称。
在主线程和呈现线程之间传递的args可以是可以用结构化克隆算法序列化的任何东西,特别是这些支撑类型。试图发送函数、承诺、符号、WeakMaps或WeakSets将无法工作并导致异常。
let validChannels = ipc.render.send从先前声明的const ipc = {...}变量中提取白通道名称。
最后,if使用的channel名称“包括”在白名单中的validChannels名称,然后调用电子的ipcRenderer.send(channel, ...args)函数。
其他两个函数,receive和invoke也是如此,注意到receive使用ipcRenderer.on(channel, listener)函数,invoke使用ipcRenderer.invoke(channel, ...args)函数。
https://stackoverflow.com/questions/71628331
复制相似问题