我试图在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相关问答推荐

Windows上使用ES6+的OpenAPI规范的Express服务器不接受嵌套路由'

验证器功能在mongoose 中不起作用

使用 playwright 获取页面中同一 url 的所有响应

我的 React + Express 应用程序不断向我的数组添加一个空对象

如何从 Mongo Atlas 触发器向 GCP PubSub 发出经过身份验证的请求

Mongodb - 在数组数组中查找()

如何使用对象中的常量值验证字符串字段?

Axios 响应循环通过函数只返回第一个映射对象的结果

为什么 $or 在带有正则表达式的mongoose 中不能正常工作

多字段传递获取查询失败

如何从动态Typescript 文件加载模块

如何从名字,中间名,姓氏中获取全名并在结果中搜索匹配?

fastify:流过早关闭

在express js模型中将js转换为Typescript 时Typescript 错误

类 WebSwapCGLLayer 在 Mac OS X /System 和 node_modules 中都实现了

Cypress net::ERR_EMPTY_RESPONSE 在真实服务器调用上

错误:大多数中间件(如 bodyParser)不再与 Express Bundle

为什么我会收到错误:解决方法指定过多?

npm package.json 操作系统特定脚本

如何断言不为空?