I'm struggling to understand how to correctly import ipcRenderer in a .vue file.
I put in /src/background.js file :
webPreferences: {
nodeIntegration:false,
contextIsolation: true, // protects against prototype pollution
preload: path.join(__dirname, "../dist_electron/preload.js"),
}
And, based on https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration I put in preload.js :
window.ipcRenderer = ipcRenderer
webpack.config.js :
module.exports = {
entry: './src/background.js',
target: 'node',
output: {
path: path.join(__dirname, 'build'),
filename: 'background.js'
}
}
In order to facilitate the debugging, I created a github repo. You can git clone the repo from here: https://github.com/raphael10-collab/ElectronVueTypeScriptScaffolding.git
After executing yarn -> yarn electron:serve you will get the correct page.
But when activating in /src/views/Home.vue this line:
//import { ipcRenderer } from 'electron'
you will get this error:
__dirname is not defined
Environment Info:
System:
OS: Linux 5.4 Ubuntu 18.04.5 LTS (Bionic Beaver)
CPU: (8) x64 Intel(R) Core(TM) i7-4790K CPU @ 4.00GHz
Binaries:
Node: 14.5.0 - ~/.nvm/versions/node/v14.5.0/bin/node
Yarn: 1.22.4 - /usr/bin/yarn
npm: 6.14.5 - ~/.nvm/versions/node/v14.5.0/bin/npm
Browsers:
Chrome: 85.0.4183.83
Firefox: 79.0
npmPackages:
@vue/babel-helper-vue-jsx-merge-props: 1.0.0
@vue/babel-plugin-transform-vue-jsx: 1.1.2
@vue/babel-preset-app: 4.4.6
@vue/babel-preset-jsx: 1.1.2
@vue/babel-sugar-functional-vue: 1.1.2
@vue/babel-sugar-inject-h: 1.1.2
@vue/babel-sugar-v-model: 1.1.2
@vue/babel-sugar-v-on: 1.1.2
@vue/cli-overlay: 4.4.6
@vue/cli-plugin-babel: ~4.4.0 => 4.4.6
@vue/cli-plugin-e2e-cypress: ~4.4.0 => 4.4.6
@vue/cli-plugin-router: ~4.4.0 => 4.4.6
@vue/cli-plugin-typescript: ~4.4.0 => 4.4.6
@vue/cli-plugin-unit-mocha: ~4.4.0 => 4.4.6
@vue/cli-plugin-vuex: ~4.4.0 => 4.4.6
@vue/cli-service: ~4.4.0 => 4.4.6
@vue/cli-shared-utils: 4.4.6
@vue/component-compiler-utils: 3.2.0
@vue/preload-webpack-plugin: 1.1.2
@vue/test-utils: ^1.0.3 => 1.0.3
@vue/web-component-wrapper: 1.2.0
babel-helper-vue-jsx-merge-props: 2.0.3
typescript: ^3.9.7 => 3.9.7
vue: ^2.6.11 => 2.6.11
vue-class-component: ^7.2.5 => 7.2.5
vue-cli-plugin-electron-builder: ~2.0.0-rc.4 => 2.0.0-rc.4
vue-hot-reload-api: 2.3.4
vue-i18n: ^8.20.0 => 8.20.0
vue-loader: 15.9.3
vue-property-decorator: ^9.0.0 => 9.0.0
vue-router: ^3.2.0 => 3.3.4
vue-style-loader: 4.1.2
vue-template-compiler: ^2.6.11 => 2.6.11
vue-template-es2015-compiler: 1.9.1
vuex: ^3.5.1 => 3.5.1
vuex-class: ^0.3.2 => 0.3.2
npmGlobalPackages:
@vue/cli: 4.4.6
node version: v14.5.0
Update 1)
I tried to set webPreferences as follows (with nodeIntegration: true) :
webPreferences: {
nodeIntegration: true,
//contextIsolation: true, // protects against prototype pollution
//preload: path.join(__dirname, "../dist_electron/preload.js"),
},
and got this error:
fs.existsSync is not a function
Searching around for info about this kind of problem, I found this post: How to resolve fs.existsSync is not a function With this link: https://webpack.js.org/concepts/targets/
But I already specified in webpack.config.js the target ‘node’:
in webpack.config.js :
module.exports = {
entry: './src/background.js',
target: 'node',
output: {
path: path.join(__dirname, 'build'),
filename: 'background.js'
}
}
So... how to solve this new problem?
By the way, Why must I put
webPreferences: {
nodeIntegration: true,
}
if, for security reasons, it is more secure to have:
webPreferences: {
nodeIntegration:false,
contextIsolation: true, // protects against prototype pollution
preload: path.join(__dirname, "../dist_electron/preload.js"),
}
dist_electron/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));
}
}
}
);
window.ipcRenderer = ipcRenderer
https://www.electronjs.org/docs/tutorial/security#electron-security-warnings
Update 2)
in vue.config.js I've put:
module.exports = {
pluginOptions: {
electronBuilder: {
preload: 'dist_electron/preload.js',
// Or, for multiple preload files:
//preload: { preload: 'src/preload.js', otherPreload:
//'src/preload2.js' }
}
}
}
But I get the same error when I do
yarn electron:serve
UncaughtReferenceError: __dirname is not defined
When setting nodeIntegration: true (but I would prefer to set it to false, and use preload.js file), I get this other error (as above):
Uncaught TypeError: fs.existsSync is not a function
Uncaught TypeError: fs.existsSync is not a function
How to solve the problem? Looking forward to your kind help
In order to use the ipcRenderer with Vue CLI plugin Electron Builder you need to first setup electron to utilize a preload.js file.
Inside your vue.config.js
file you need to add the preload.js
path like this:
// vue.config.js - project root
module.exports = {
pluginOptions: {
electronBuilder: {
preload: 'src/preload.js',
// Or, for multiple preload files:
preload: { preload: 'src/preload.js', otherPreload: 'src/preload2.js' }
}
}
}
Next you need to updated your background.js
file to use preload.js
in the web preferences like this:
// src/background.js
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
// Use pluginOptions.nodeIntegration, leave this alone
// See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
enableRemoteModule: true,
preload: path.join(__dirname, 'preload.js'),
},
})
Note:
nodeIntegration
is disabled andcontextIsolation
is enabled by default
Once you have that complete you can create the preload.js
file in your src directory.
With contextIsolation
enabled you need to import the contextBridge
along with ipcRenderer
. Then you can can expose the ipcRenderer
to your client.
Then add this to the file:
// src/preload.js
import { contextBridge, ipcRenderer } from 'electron'
// Expose ipcRenderer to the client
contextBridge.exposeInMainWorld('ipcRenderer', {
send: (channel, data) => {
let validChannels = ['nameOfClientChannel'] // <-- Array of all ipcRenderer Channels used in the client
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data)
}
},
receive: (channel, func) => {
let validChannels = ['nameOfElectronChannel'] // <-- Array of all ipcMain Channels used in the electron
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`
ipcRenderer.on(channel, (event, ...args) => func(...args))
}
}
})
Note: You need to make sure your
preload.js
file is in thesrc
folder and notdist_electron
To test and make sure the preload file is working you can also create an alert in the preload.js
file
// src/preload.js
import { contextBridge, ipcRenderer } from 'electron'
// Expose ipcRenderer to the client
contextBridge.exposeInMainWorld('ipcRenderer', {
send: (channel, data) => {
let validChannels = ['nameOfClientChannel'] // <-- Array of all ipcRenderer Channels used in the client
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data)
}
},
receive: (channel, func) => {
let validChannels = ['nameOfElectronChannel'] // <-- Array of all ipcMain Channels used in the electron
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`
ipcRenderer.on(channel, (event, ...args) => func(...args))
}
}
})
alert("It Worked!") // Remove this line once you confirm it worked
When you have verified that your preload script is working correctly, you can access the ipcRenderer
from your vue app.
like this:
// src/App.vue
<template>
\\ Some html
</template>
<script>
export default {
name: "App",
methods: {
test(){
window.ipcRenderer.send(channel, args...) // or any other ipcRenderer method you want to invoke
}
};
</script>
In electron you can listen for those events
// background.js
ipcMain.on(channel, (event, args) => {
// Do stuff
});
Sources:
- Preload Files
- Node Integration
- With contextIsolation = true, is it possible to use ipcRenderer?
In order to use the ipcRenderer with Vue CLI plugin Electron Builder you need to first setup electron to utilize a preload.js file.
Inside your vue.config.js
file you need to add the preload.js
path like this:
// vue.config.js - project root
module.exports = {
pluginOptions: {
electronBuilder: {
preload: 'src/preload.js',
// Or, for multiple preload files:
preload: { preload: 'src/preload.js', otherPreload: 'src/preload2.js' }
}
}
}
Next you need to updated your background.js
file to use preload.js
in the web preferences like this:
// src/background.js
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
// Use pluginOptions.nodeIntegration, leave this alone
// See nklayman.github.io/vue-cli-plugin-electron-builder/guide/configuration.html#node-integration for more info
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
+ preload: path.join(__dirname, 'preload.js')
}
})
Once you have that complete you can create the preload.js
file in your src directory
Then add this to the file:
// src/preload.js
import { ipcRenderer } from 'electron'
window.ipcRenderer = ipcRenderer
Note: You need to make sure your
preload.js
file is in thesrc
folder and notdist_electron
To test and make sure the preload file is working you can also create an alert in the preload.js
file
// src/preload.js
import { ipcRenderer } from 'electron'
window.ipcRenderer = ipcRenderer
alert("It Worked!") // Remove this line once you confirm it worked
When you have verified that your preload script is working correctly, you can access the ipcRenderer from your vue app.
like this:
// src/App.vue
<template>
\\ Some html
</template>
<script>
export default {
name: "App",
methods: {
test(){
window.ipcRenderer.send(channel, args...) // or any other ipcRenderer method you want to invoke
}
};
</script>
Sources:
- Preload Files
- Node Integration
What worked for me was setting the electron window's contextIsolation
to false
.
So in your main.js
wherever you create a BrowserWindow
it would look like this:
const win = new BrowserWindow({
webPreferences: {
contextIsolation: false,
preload: path.join(__dirname, 'preload.js'),
},
})
And then in preload.js
you can simply do
const { ipcRenderer } = require('electron')
window.ipcRenderer = ipcRenderer
And then you'll have access to ipcRenderer
anywhere in your vue code.
It seems that in the current version of electron contextIsolation
defaults to true
, which makes the window
that preload.js
sees different from the one your vue app sees.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With