node.js - Vue Electron 在异步调用一些 Node 库时卡住了
问题描述
语境
我正在尝试使用以下堆栈构建应用程序:
我已经开发了一段时间的简单 Vue 应用程序,并且我了解了基础知识。我在这里尝试做的是使用这个很棒的工具将 Vue 和 Electron 结合起来:Vue CLI Plugin Electron Builder。
虽然我已经成功地使用这个堆栈编写了简单的应用程序,但在利用 Electron 的nodeIntegration时我遇到了问题,它应该让 Node 直接访问我的 Vue 代码。
问题
我对系统信息库的某些方法的异步调用似乎被卡住了(有时)。也就是说,当我想通过调用系统信息库的异步方法来为我的视图数据赋值时,Vue 似乎无限期地挂起等待。
奇怪的是,有时,当我强制刷新页面时,我可以在控制台中简要地看到该方法返回数据时应该输出的日志。这几乎就像刷新页面强制 Vue 或其他东西更新一样。
我怀疑以下线索,但我可能错了:
- Vue 在我的声明和/或分配方式中的反应性问题。
- 节点集成问题使 Vue+Node 无法正确“绑定”。
- Electron 对 Node 库的打包缺少要在 Vue 中使用的配置,“就好像它是一个常规的 JS 库一样”。
后一点我不清楚,因为我一直使用 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 1和Scenario 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
钩子卡住了。
解决方案
我刚刚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' }
}
推荐阅读
- c++ - Program won't recognize all functions from my header file (Except two of them)
- docker - 如何将大量数据放入 docker 镜像中?
- extjs - extjs how to pass parameters to a web rest api request
- sql-server - 就地 SQL 升级和 SSIS
- gradle - 重复生成的类 gradle 输出(build/...)与 intellij 输出(out/...)
- python - Django Rest Framework 将不接受我的 CSRF 令牌
- python - 如何使用 CEFPython 在 Hi-DPI 屏幕上的同一窗口中停止 html 渲染两次?
- javascript - 尝试按云量百分比过滤前哨 2 图像
- plotly - plotly store 组件中使用的`modified_timestamp` 是什么
- java - 如何在 Spring Boot 应用程序中测试验证器?