首页 > 解决方案 > Vue Electron 在异步调用一些 Node 库时卡住了

问题描述

语境

我正在尝试使用以下堆栈构建应用程序:

我已经开发了一段时间的简单 Vue 应用程序,并且我了解了基础知识。我在这里尝试做的是使用这个很棒的工具将 Vue 和 Electron 结合起来:Vue CLI Plugin Electron Builder

虽然我已经成功地使用这个堆栈编写了简单的应用程序,但在利用 Electron 的nodeIntegration时我遇到了问题,它应该让 Node 直接访问我的 Vue 代码。

问题

我对系统信息库的某些方法的异步调用似乎被卡住了(有时)。也就是说,当我想通过调用系统信息库的异步方法来为我的视图数据赋值时,Vue 似乎无限期地挂起等待。

奇怪的是,有时,当我强制刷新页面时,我可以在控制台中简要地看到该方法返回数据时应该输出的日志。这几乎就像刷新页面强制 Vue 或其他东西更新一样。

我怀疑以下线索,但我可能错了:

后一点我不清楚,因为我一直使用 Electron+Vue 访问 Node 库,就像访问 Web 库一样。可能还有更多可能导致这些问题,但据我所知……

重现步骤

您可以按如下方式创建 Vue+Electron:

vue create myapp
vue add electron-builder
yarn add systeminformation

我将保留 Vue CLI 的交互式项目创建的默认值,因为它们对我的问题没有影响。

根据 Vue CLI Plugin Electron Builder 关于nodeIntegration的文档,我的vue.config.js文件如下所示:

module.exports = {
  pluginOptions: {
    electronBuilder: {
      nodeIntegration: true,
    },
  },
};

然后,您可以使用Scenario 1Scenario 2中的片段。

方案 1

让我们考虑以下示例,其中我使用Axios分配值:

<template>
  <pre>{{ test }}</pre>
</template>
<script>
  import axios from "axios";
  export default {
    data() {
      return {
        test: null,
      };
    },
    async mounted() {
      console.log("Getting data from Axios...");
      // Assign a value to the "test" data with an asynchronous HTTP call with Axios.
      this.test = (
        await axios.get("https://cat-fact.herokuapp.com/facts")
      ).data;
      // It gets there as soon as the server responds.
      console.log("Got it!", this.test);
    },
  };
</script>

在这里,异步调用按预期工作,我的数据正确更新并在Axios调用得到答案test后立即显示在我的视图中。

方案 2

现在,如果我使用相同的逻辑从系统信息库的方法中获取数据

<template>
  <pre>{{ test }}</pre>
</template>
<script>
  import systeminformation from "systeminformation";
  export default {
    data() {
      return {
        test: null,
      };
    },
    async mounted() {
      console.log("Getting data from systeminformation...");
      // Assign a value to the "test" data with an asynchronous call to a method of systeminformation.
      this.test = await systeminformation.getStaticData();
      // It just won't get there most of the time...
      console.log("Got it!", this.test);
    },
  };
</script>

在这种情况下,我的视图不会显示数据,因为 Vue 似乎无限期地test挂在systeminformation调用上。控制台日志甚至不会显示,因为 await 语句似乎使mounted钩子卡住了。

标签: node.jsvuejs2electron

解决方案


我刚刚systeminformation.getStaticData()在自己的 Electron + Vue 应用程序中测试了函数,结果如下:

它异步执行代码,但操作非常繁重,使应用程序几乎完全没有响应。它反复产生约 30 个 Node 子进程,有效地阻塞了 3-4 个 CPU 线程。

我认为这可能是getStaticData()功能的错误。您可能想在他们的仓库中创建一个新问题并报告它。

解决方案

要么不要使用这个特定的功能,而是通过运行其他systeminformation功能来获取所有需要的信息。

或者

在工作人员中启用节点集成:

webPreferences: {
  nodeIntegration: true,
  nodeIntegrationInWorker: true
}

在工作线程中执行此函数,而不是在主线程上,并将结果发送回您的组件,然后终止工作线程。虽然当你这样做时,打开任务管理器并确保它终止所有产生的子进程:

Vue 组件

import sysInfoWorker from 'worker-loader!./workers/sysInfoWorker'

data () {
  return {
    sysInfoWorker: null
  }
},
mounted () {
  this.initSysInfoWorker()
},
methods: {
  initSysInfoWorker () {
    this.sysInfoWorker = new sysInfoWorker()
    try {
      this.sysInfoWorker.onmessage = (event) => {
        console.log('sysInfoWorker message:', event)
        this.test = event
      }
      this.sysInfoWorker.onerror = (error) => {
        console.log('sysInfoWorker error:', error)
      }
    } 
    catch (error) {
      console.log(error)
    }
  },
  startSysInfoWorker () {
    this.sysInfoWorker.postMessage({ 
      operation: 'run:getStaticData'
    })
  },
  cancelSysInfoWorker () {
    this.sysInfoWorker.postMessage('cancel:getStaticData')
    // this.sysInfoWorker.terminate()
  }
}

工人

const systeminformation = require('systeminformation')

const state = { cancelled: false }

// Listen to messages from parent thread
self.addEventListener('message', (event) => {
  if (event.data === 'cancel:getStaticData') {
    state.cancelled = true
  }
  else {
    state.cancelled = false
    initWorker(event)
  }
})

async function initWorker (event) {
  if (event.data.operation === 'run:getStaticData') {
    const info = await systeminformation.getStaticData()
    self.postMessage({ info: info })
  }

}

如果工作人员抛出错误,请尝试将以下内容添加到您的vue.config

module.exports = {
  configureWebpack: {
    module: {
      rules: [
        {
          test: /\.worker\.js$/,
          use: { loader: 'worker-loader' }
        }

推荐阅读