【问题标题】:How to import ipcRenderer in vue.js ? __dirname is not defined如何在 vue.js 中导入 ipcRenderer? __dirname 未定义
【发布时间】:2020-12-16 07:11:58
【问题描述】:

我很难理解如何在 .vue 文件中正确导入 ipcRenderer。

我放入 /src/background.js 文件:

webPreferences: {
  nodeIntegration:false,
  contextIsolation: true, // protects against prototype pollution
  preload: path.join(__dirname, "../dist_electron/preload.js"),
}

并且,基于https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration,我输入了 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'
  }
}

为了方便调试,我创建了一个github repo。 你可以从这里 git clone 仓库:https://github.com/raphael10-collab/ElectronVueTypeScriptScaffolding.git

执行yarn后->yarn electron:serve 你会得到正确的页面。

但是当在 /src/views/Home.vue 中激活这一行时:

//从“电子”导入 { ipcRenderer }

你会得到这个错误:

__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

更新 1)

我尝试将 webPreferences 设置如下(使用 nodeIntegration: true):

webPreferences: {
  nodeIntegration: true,
  //contextIsolation: true, // protects against prototype pollution
  //preload: path.join(__dirname, "../dist_electron/preload.js"),
},

得到了这个错误:

fs.existsSync is not a function

四处搜索有关此类问题的信息,我发现了这篇文章: How to resolve fs.existsSync is not a function 通过此链接:https://webpack.js.org/concepts/targets/

但我已经在 webpack.config.js 中指定了目标“节点”:

在 webpack.config.js 中:

module.exports = {
  entry: './src/background.js',
  target: 'node',
  output: {
    path: path.join(__dirname, 'build'),
    filename: 'background.js'
  }
}

那么...如何解决这个新问题?

顺便说一句, 为什么一定要放

webPreferences: {
    nodeIntegration: true,
} 

如果出于安全原因,更安全的是:

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

更新 2)

在 vue.config.js 我放了:

module.exports = {
  pluginOptions: {
    electronBuilder: {
      preload: 'dist_electron/preload.js',
      // Or, for multiple preload files:
      //preload: { preload: 'src/preload.js', otherPreload: 
      //'src/preload2.js' }
    }
  }
}

但是当我这样做时,我得到了同样的错误

yarn electron:serve

UncaughtReferenceError: __dirname is not defined

在设置 nodeIntegration: true 时(但我更愿意将其设置为 false,并使用 preload.js 文件),我得到另一个错误(如上):

Uncaught TypeError: fs.existsSync is not a function

Uncaught TypeError: fs.existsSync is not a function

如何解决问题? 期待您的帮助

【问题讨论】:

    标签: javascript typescript vue.js electron ipcrenderer


    【解决方案1】:

    更新答案 - 禁用 Nodeintegration 并启用 contextIsolation

    为了将 ipcRenderer 与 Vue CLI 插件 Electron Builder 一起使用,您需要首先设置 electron 以使用 preload.js 文件。

    在您的vue.config.js 文件中,您需要像这样添加preload.js 路径:

    // 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' }
        }
      }
    }
    

    接下来,您需要更新您的background.js 文件以在网络首选项中使用preload.js,如下所示:

    // 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'),
      },
    })
    

    注意:nodeIntegration 默认禁用,contextIsolation 默认启用

    完成后,您可以在 src 目录中创建 preload.js 文件。
    启用contextIsolation 后,您需要导入contextBridgeipcRenderer。然后您可以将ipcRenderer 公开给您的客户。

    然后将其添加到文件中:

    // 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))
        }
      }
    })
    

    注意:您需要确保您的preload.js 文件位于src 文件夹中,而不是dist_electron

    要测试并确保预加载文件正常工作,您还可以在 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
    
    

    当您验证您的预加载脚本工作正常后,您可以从您的 vue 应用访问ipcRenderer

    像这样:

    // 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>
    
    

    在电子中你可以监听这些事件

    // background.js
    ipcMain.on(channel, (event, args) => {
      // Do stuff
    });
    

    来源:
    https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/guide.html#preload-files https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration With contextIsolation = true, is it possible to use ipcRenderer?

    原答案

    为了将 ipcRenderer 与 Vue CLI 插件 Electron Builder 一起使用,您需要首先设置 electron 以使用 preload.js 文件。

    在您的vue.config.js 文件中,您需要像这样添加preload.js 路径:

    // 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' }
        }
      }
    }
    

    接下来,您需要更新您的background.js 文件以在网络首选项中使用preload.js,如下所示:

    // 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')
      }
    })
    

    完成后,您可以在 src 目录中创建 preload.js 文件

    然后将其添加到文件中:

    // src/preload.js
    
    import { ipcRenderer } from 'electron'
    window.ipcRenderer = ipcRenderer
    

    注意:您需要确保您的preload.js 文件位于src 文件夹中,而不是dist_electron

    要测试并确保预加载文件正常工作,您还可以在 preload.js 文件中创建警报

    // src/preload.js
    
    import { ipcRenderer } from 'electron'
    window.ipcRenderer = ipcRenderer
    
    alert("It Worked!") // Remove this line once you confirm it worked
    
    

    当您确认您的预加载脚本工作正常后,您可以从您的 vue 应用访问 ipcRenderer。

    像这样:

    // 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>
    
    

    来源:
    https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/guide.html#preload-files https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration

    【讨论】:

    • 为我工作,但这是一些疯狂的样板......
    • 此解决方案来自 Vue CLI Plugin Electron Builder 文档。 nklayman.github.io/vue-cli-plugin-electron-builder/guide/…读了好久,不过看了几遍还是挺直接的。
    • 谢谢,是的,该文档很有帮助,我希望他们能像您一样展示一个使用共享窗口对象的示例,这在 IMO 部分中有点差距。
    • 不起作用console.log( window.ipcRenderer) = 未定义:|
    • @A1Gard 您能否针对您遇到的问题提出一个新问题并将其链接到此处。我会尽力提供帮助。
    【解决方案2】:

    对我有用的是将电子窗口的contextIsolation 设置为false

    所以在您的main.js 中,无论您创建BrowserWindow,它都会如下所示:

    const win = new BrowserWindow({
      webPreferences: {
        contextIsolation: false,
        preload: path.join(__dirname, 'preload.js'),
      },
    })
    

    然后在preload.js 你可以简单地做

    const { ipcRenderer } = require('electron')
    window.ipcRenderer = ipcRenderer
    

    然后你就可以在你的 vue 代码的任何地方访问ipcRenderer

    似乎在当前版本的电子contextIsolation 中默认为true,这使得preload.js 看到的window 与您的vue 应用看到的不同。

    【讨论】:

    • 不要这样做,您正在关闭安全措施并使用不推荐的方法来考虑 IPC 用于 Renderer 进程,而它应该能够使用之前定义的相同 IPC名字。
    【解决方案3】:

    您需要将nodeIntegration 设置为true

    这会在渲染器进程(即前端)中启用 NodeJ,因此您可以在 Vue 代码中使用 fs(文件系统)和其他仅限 NodeJ 的功能。

    由于 ipcRenderer 也需要 NodeJs 的环境(__dirname 只是 NodeJs 的全局变量),所以需要激活它。

    【讨论】:

    • 嗨@Oussama!感谢您的帮助!!我用更新 1 更新了我的帖子)
    • 嗨,是的,您对 preload.js 解决方案是正确的,它更安全,但缺点是您要将逻辑放在 public/ 文件夹中,这不是很理想。至于错误,因为您使用的是vue-cli-plugin-electron-builder,我建议您尝试在vue.config.js 中添加您的配置,如文档中所述:nklayman.github.io/vue-cli-plugin-electron-builder/guide/… 而且,为了确定,请确保您正在运行您的应用程序使用 yarn electron:serve 而不是在浏览器中。
    • 我在尝试运行我的应用程序时一直在使用 yarn electron:serve。我用更新 2) 更新了我的帖子。感谢您的帮助。
    猜你喜欢
    • 2020-09-30
    • 2021-03-05
    • 2018-03-14
    • 2018-06-17
    • 1970-01-01
    • 2021-01-30
    • 1970-01-01
    • 2021-07-01
    • 2021-08-29
    相关资源
    最近更新 更多