javascript - ¿Cómo importar ipcRenderer en vue.js? __dirname no está definido
Estoy teniendo dificultades para entender cómo importar correctamente ipcRenderer en un archivo .vue.
Puse en el archivo /src/background.js :
webPreferences: {
nodeIntegration:false,
contextIsolation: true, // protects against prototype pollution
preload: path.join(__dirname, "../dist_electron/preload.js"),
}
Y, basado en https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration puse en 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'
}
}
Para facilitar la depuración, creé un repositorio en GitHub. Puedes clonar el repositorio usando git desde aquí: https://github.com/raphael10-collab/ElectronVueTypeScriptScaffolding.git
Después de ejecutar yarn -> yarn electron:serve obtendrás la página correcta.
Pero al activar en /src/views/Home.vue esta línea:
//importar { ipcRenderer } desde 'electron'
obtendrás este error:
__dirname no está definido
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
Actualización 1)
Intenté configurar webPreferences de la siguiente manera (con nodeIntegration: true):
webPreferences: {
nodeIntegration: true,
//contextIsolation: true, // protects against prototype pollution
//preload: path.join(__dirname, "../dist_electron/preload.js"),
},
y obtuve este error:
fs.existsSync no es una función
Buscando información sobre este tipo de problema, encontré esta publicación: Cómo resolver fs.existsSync no es una función Con este enlace: https://webpack.js.org/concepts/targets/
Pero ya especifiqué en webpack.config.js el objetivo ‘node’:
en webpack.config.js :
module.exports = {
entry: './src/background.js',
target: 'node',
output: {
path: path.join(__dirname, 'build'),
filename: 'background.js'
}
}
Entonces... ¿cómo resolver este nuevo problema?
Por cierto, ¿por qué debo poner
webPreferences: {
nodeIntegration: true,
}
si, por razones de seguridad, es más seguro tener:
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
Actualización 2)
en vue.config.js he puesto:
module.exports = {
pluginOptions: {
electronBuilder: {
preload: 'dist_electron/preload.js',
// Or, for multiple preload files:
//preload: { preload: 'src/preload.js', otherPreload:
//'src/preload2.js' }
}
}
}
Pero obtengo el mismo error cuando lo hago
yarn electron:serve
Error de referencia no capturado: __dirname no está definido
Al configurar nodeIntegration: true (pero preferiría configurarlo en false y usar un archivo preload.js), recibo este otro error (como se indicó arriba):
Uncaught TypeError: fs.existsSync is not a function
Error de tipo no detectado: fs.existsSync no es una función
¿Cómo resolver el problema? Espero con interés su amable ayuda
Respuesta #1
Respuesta actualizada - Nodeintegration deshabilitado y contextIsolation habilitado
Para usar el ipcRenderer con el plugin Vue CLI Electron Builder, primero necesitas configurar Electron para utilizar un archivo preload.js.
Dentro de tu archivo vue.config.js necesitas agregar la ruta de preload.js así:
// 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' }
}
}
}
A continuación, necesitas actualizar tu archivo background.js para usar preload.js en las preferencias web de esta manera:
// 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'),
},
})
Nota:
nodeIntegrationestá deshabilitado ycontextIsolationestá habilitado por defecto
Una vez que hayas completado eso, puedes crear el archivo preload.js en tu directorio src.
Con contextIsolation habilitado, necesitas importar contextBridge junto con ipcRenderer. Luego puedes exponer el ipcRenderer a tu cliente.
Luego añade esto al archivo:
// 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))
}
}
})
Nota: Debes asegurarte de que tu archivo
preload.jsesté en la carpetasrcy no endist_electron
Para probar y asegurarse de que el archivo de precarga funciona, también puede crear una alerta en el archivo preload.js
// 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
Cuando hayas verificado que tu script de precarga funciona correctamente, puedes acceder a ipcRenderer desde tu aplicación vue.
así:
// 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>
En Electron puedes escuchar esos eventos
// background.js
ipcMain.on(channel, (event, args) => {
// Do stuff
});
Fuentes:
- Precargar Archivos
- Integración de Nodo
- Con contextIsolation=true, ¿es posible usar ipcRenderer?
Respuesta original
Para usar el ipcRenderer con el plugin Vue CLI Electron Builder, primero necesitas configurar Electron para utilizar un archivo preload.js.
Dentro de tu archivo vue.config.js necesitas agregar la ruta de preload.js así:
// 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' }
}
}
}
A continuación, necesitas actualizar tu archivo background.js para usar preload.js en las preferencias web de esta manera:
// 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')
}
})
Una vez que hayas completado eso, puedes crear el archivo preload.js en tu directorio src
Luego añade esto al archivo:
// src/preload.js
import { ipcRenderer } from 'electron'
window.ipcRenderer = ipcRenderer
Nota: Debes asegurarte de que tu archivo
preload.jsesté en la carpetasrcy no endist_electron
Para probar y asegurarse de que el archivo de precarga funciona, también puede crear una alerta en el archivo preload.js
// src/preload.js
import { ipcRenderer } from 'electron'
window.ipcRenderer = ipcRenderer
alert("It Worked!") // Remove this line once you confirm it worked
Cuando hayas verificado que tu script de precarga funciona correctamente, puedes acceder al ipcRenderer desde tu aplicación Vue.
así:
// 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>
Fuentes:
- Precargar Archivos
- Integración de Nodo
Respuesta #2
Lo que me funcionó fue configurar el contextIsolation de la ventana de Electron a false.
Entonces, en tu main.js, donde sea que crees un BrowserWindow, se vería así:
const win = new BrowserWindow({
webPreferences: {
contextIsolation: false,
preload: path.join(__dirname, 'preload.js'),
},
})
Y luego en preload.js puedes simplemente hacer
const { ipcRenderer } = require('electron')
window.ipcRenderer = ipcRenderer
Y luego tendrás acceso a ipcRenderer en cualquier parte de tu código de Vue.
Parece que en la versión actual de Electron contextIsolation tiene como valor predeterminado true, lo que hace que el window que preload.js ve sea diferente del que ve tu aplicación Vue.
