Electron require() is not defined

2020-01-27 02:16发布

问题:

I'm creating an Electron app for my own purpose. My problem is when I'm using node functions inside my HTML page it throws an error of:

'require()' is not defined.

Is there any way to use Node functionalities in all my HTML pages? If it is possible please give me an example of how to do this or provide a link. Here are the variables I'm trying to use in my HTML page:

  var app = require('electron').remote; 
  var dialog = app.dialog;
  var fs = require('fs');

and these are the values I'm using in all my HTML windows within Electron.

回答1:

As of version 5, the default for nodeIntegration changed from true to false. You can enable it when creating the Browser Window:

app.on('ready', () => {
    mainWindow = new BrowserWindow({
        webPreferences: {
            nodeIntegration: true
        }
    });
});


回答2:

For security reasons, you should keep nodeIntegration: false and use a preload script to expose just what you need from Node/Electron API to the renderer process (view) via window variable. From the Electron docs:

Preload scripts continue to have access to require and other Node.js features


Example

main.js

const mainWindow = new BrowserWindow({
  webPreferences: {
    preload: path.join(app.getAppPath(), 'preload.js')
  }
})

preload.js

const { remote } = require('electron');

let currWindow = remote.BrowserWindow.getFocusedWindow();

window.closeCurrentWindow = function(){
  currWindow.close();
}

renderer.js

let closebtn = document.getElementById('closebtn');

closebtn.addEventListener('click', (e) => {
  e.preventDefault();
  window.closeCurrentWindow();
});


回答3:

Are you using nodeIntegration: false while BrowserWindow initialization? If so, set it to true (defaults value is true).

And include your external scripts in the HTML like this (not as <script> src="./index.js" </script>):

<script>
   require('./index.js')
</script>


回答4:

First off, @Sathiraumesh solution leaves your electron application with huge security issue. Imagine that your app is adding some extra features to messenger.com, for example toolbar's icon will change or blink when you've have unread message. So in your main.js file, you create new BrowserWindow like so (notice I intentionally misspelled messenger.com):

app.on('ready', () => {
    const mainWindow = new BrowserWindow({
        webPreferences: {
            nodeIntegration: true
        }
    });
    mainWindow.loadURL(`https://messengre.com`);
});

What if messengre.com is a malicious website, that wants to harm your computer. If you set nodeIntegration: true this site has access to your local file system and can execute this:

require('child_process').exec('rm -r ~/');

And your home directory is gone.

Solution
Expose only what you need, instead of everything. This is achived by preloading javascript code with require statements.

// main.js
app.on('ready', () => {
    const mainWindow = new BrowserWindow({
        webPreferences: {
            preload: `${__dirname}/preload.js`
        }
    });
    mainWindow.loadURL(`https://messengre.com`);
});
// preload.js
window.ipcRenderer = require('electron').ipcRenderer;
// index.html
<script>
    window.ipcRenderer.send('channel', data);
</script>

Now awful messengre.com cannot delete your entire file system.



回答5:

I hope this answer gets some attention, because a large majority of answers here leave large security holes in your electron app. In fact this answer is essentially what you should be doing to use require() in your electron apps. (There is just a new electron API that makes it a little bit cleaner in v7).

I wrote a detailed explanation/solution in github using the most current electron apis of how you can require() something, but I'll explain briefly here why you should follow an approach using a preload script, contextBridge and ipc.

The problem

Electron apps are great because we get to use node, but this power is a double-edged sword. If we are not careful, we give someone access to node through our app, and with node a bad actor can corrupt your machine or delete your operating system files (among other things, I imagine).

What does the problem look like

This problem manifests when you (any one of the below):

  1. Have nodeIntegration:true enabled
  2. Use the remote module

All of these problems give uninterrupted access to node from your renderer process. If your renderer process is ever hijacked, you can consider all is lost.

What our solution is

The solution is to not give the renderer direct access to node (ie. require()), but to give our electron main process access to require, and anytime our renderer process needs to use require, marshal a request to the main process.

The way this works in the latest versions (7+) of Electron is on the renderer side we set up ipcRenderer bindings, and on the main side we set up ipcMain bindings. In the ipcMain bindings we set up listener methods that use modules we require(). This is fine and well because our main process can require all it wants.

We use the contextBridge to pass the ipcRenderer bindings to our app code (to use), and so when our app needs to use the required modules in main, it sends a message via IPC (inter-process-communication) and the main process runs some code, and we then send a message back with our result.

Roughly, here's what you want to do.

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) => fn(...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>

Disclaimer

I'm the author of secure-electron-template, a secure template to build electron apps. I care about this topic, and have been working on this for a few weeks (at this point in time).



回答6:

Finally, I made it work.Add this code to your HTML document Script Element.

Sorry for the late Reply.I use the below code to do this thing.

window.nodeRequire = require;
delete window.require;
delete window.exports;
delete window.module;

And use nodeRequire instead of using require.

It works Fine.



回答7:

You have to enable the nodeIntegration in webPreferences to use it. see below,

const { BrowserWindow } = require('electron')
let win = new BrowserWindow({
  webPreferences: {
    nodeIntegration: true
  }
})
win.show()

There was a breaking api changes in electron 5.0(Announcement on Repository). In recent versions nodeIntegration is by default set to false.

Docs Due to the Node.js integration of Electron, there are some extra symbols inserted into the DOM like module, exports, require. This causes problems for some libraries since they want to insert the symbols with the same names.To solve this, you can turn off node integration in Electron:

But if you want to keep the abilities to use Node.js and Electron APIs, you have to rename the symbols in the page before including other libraries:

<head>
    <script>
        window.nodeRequire = require;
        delete window.require;
        delete window.exports;
        delete window.module;
    </script>
    <script type="text/javascript" src="jquery.js"></script>
</head>