我试图在renderer进程中使用 node 模块(在本例中为fs),如下所示:

// main_window.js
const fs = require('fs')

function action() {
    console.log(fs)
}

注意:当我按下main_window中的一个按钮时,会调用action函数

但这会产生一个错误:

Uncaught ReferenceError: require is not defined
    at main_window.js:1

我可以通过在初始化main_window时将这些行添加到我的main.js中来解决这个问题,as suggested by this accepted answer:

// main.js
main_window = new BrowserWindow({
    width: 650,
    height: 550,
    webPreferences: {
        nodeIntegration: true
    }
})

但是,according to the docs,这不是最好的做法,我应该创建一个preload.js文件,在那里加载这些 node 模块,然后在我的renderer个进程中使用它.这样地:

main.js:

main_window = new BrowserWindow({
    width: 650,
    height: 550,
    webPreferences: {
        preload: path.join(app.getAppPath(), 'preload.js')
    }
})

preload.js:

const fs = require('fs')

window.test = function() {
    console.log(fs)
}

main_window.js:

function action() {
    window.test()
}

而且很有效!


现在我的问题是,我应该在preload.js中编写renderer个进程的大部分代码(因为只有在preload.js中我才能访问 node 模块),然后只调用每个renderer.js文件中的函数(例如,这里是main_window.js),这不是违反直觉的吗?我在这里不明白什么?

推荐答案

编辑

正如另一位用户所问,让我在下面解释我的答案.

使用preload.js英寸Electron的正确方法是在应用程序可能需要安装的任何模块周围expose 白名单包装.

在安全方面,expose require或通过preload.js中的require调用检索到的任何内容都是危险的(更多解释请参见my comment here).如果你的应用程序加载远程内容,这一点尤其正确,很多应用程序都会加载远程内容.

为了把事情做好,你需要在你的BrowserWindow上启用很多选项,我将在下面详细介绍.设置这些选项将强制您的Electron 应用程序通过IPC(进程间通信)进行通信,并将这两个环境彼此隔离.通过这样设置应用程序,您可以验证后端中可能是require'd模块的任何内容,而客户端不会对其进行篡改.

下面,你会发现一个简短的例子,我说的是什么,以及它在你的应用程序中的外观.如果你刚刚开始,我可能会建议你使用secure-electron-template(我是这本书的作者),它在构建Electron 应用程序时,从一开始就具备了所有这些安全最佳实践.

This page还提供了有关使用预加载时所需架构的良好信息.js来制作安全的应用程序.


main.js

const {
  app,
  BrowserWindow,
  ipcMain
} = require("electron");
const path = require("path");
const fs = require("fs");

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win;

async function createWindow() {

  // Create the browser window.
  win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: false, // is default value after Electron v5
      contextIsolation: true, // protect against prototype pollution
      enableRemoteModule: false, // turn off remote
      preload: path.join(__dirname, "preload.js") // use a preload script
    }
  });

  // Load app
  win.loadFile(path.join(__dirname, "dist/index.html"));

  // rest of code..
}

app.on("ready", createWindow);

ipcMain.on("toMain", (event, args) => {
  fs.readFile("path/to/file", (error, data) => {
    // Do something with file contents

    // Send result back to renderer process
    win.webContents.send("fromMain", responseObj);
  });
});

preload.js

const {
    contextBridge,
    ipcRenderer
} = require("electron");

// Expose protected methods that allow the renderer process to use
// the ipcRenderer without exposing the entire object
contextBridge.exposeInMainWorld(
    "api", {
        send: (channel, data) => {
            // whitelist channels
            let validChannels = ["toMain"];
            if (validChannels.includes(channel)) {
                ipcRenderer.send(channel, data);
            }
        },
        receive: (channel, func) => {
            let validChannels = ["fromMain"];
            if (validChannels.includes(channel)) {
                // Deliberately strip event as it includes `sender` 
                ipcRenderer.on(channel, (event, ...args) => func(...args));
            }
        }
    }
);

index.html

<!doctype html>
<html lang="en-US">
<head>
    <meta charset="utf-8"/>
    <title>Title</title>
</head>
<body>
    <script>
        window.api.receive("fromMain", (data) => {
            console.log(`Received ${data} from main process`);
        });
        window.api.send("toMain", "some data");
    </script>
</body>
</html>

Node.js相关问答推荐

意外标识符断言Shopify Remix应用程序

链接Inbox类方法,范围在哪里以及为什么发生变化?

如何使用updateMany()和Node.js的方法?

如何解决无法获得本地颁发者证书的问题

@nuxtjs/站点 map 错误提示:找不到包';NitroPack';

自动将Selify打开的Chrome窗口移动到Mac OS中的第三个显示器

Mongoose-不会更新数组中的属性值

Gmail API获取附件PDF未正确呈现.我遗漏了什么?

创建查询以展开数组并筛选出具有特定值的元素之后的元素

如何发送比特币BTC使用发送加密使用WIF密钥在 node ,js

PM2 是否需要成为其托管项目的依赖项?

动态设置元数据,无需重复请求 NextJS 13

如何在 JavaScript 中显示多维数组中使用的一维数组的变量名?

有没有办法判断 UUID 是否是使用 node.js 中的特定命名空间生成的?

每个数组值在 mongodb 中查找一个文档

如何在 cypress 测试中进行计算

如何在 mongoDB 中进行全动态搜索?

如何获取在 NodeJS 中执行的脚本的文件名?

使用restify时如何支持cors

调用 require 时的 const vs let