Búsqueda de sitios web

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: nodeIntegration está deshabilitado y contextIsolation está 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.js esté en la carpeta src y no en dist_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.js esté en la carpeta src y no en dist_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.

Artículos relacionados