I'm trying to build an app using the following stack:
I've developed simple Vue apps for a while now and I understand the basics. What I'm trying to do here is to combine Vue and Electron using this awesome tool: Vue CLI Plugin Electron Builder.
While I've successfully managed to code simple apps with this stack, I'm facing issues when it comes to exploit Electron's nodeIntegration which is supposed to give Node access directly to my Vue code.
My asynchronous calls to some methods of the systeminformation library seem to be stuck (sometimes). That is, when I want to assign a value my view's data by calling an asynchronous method of the systeminformation library, Vue seems to hang and await indefinitely.
The weired thing is that, sometimes, when I force refresh the page, I can briefly see in my console the log supposed to be output when the method returns data. It's almost like refreshing the page forces Vue or something else to update.
I'd suspect the following leads but I might be wrong:
The latter point is unclear to me as I've always used Electron+Vue to access Node libraries the same way I'd do for web libraries. There might more to it that could lead to these issues but this is as far as my knowledge goes...
You can create a Vue+Electron as follows:
vue create myapp
vue add electron-builder
yarn add systeminformation
I'm leaving default values for Vue CLI's interactive project creation as they don't have an impact on my issue.
As per Vue CLI Plugin Electron Builder's documentation on nodeIntegration, my vue.config.js file looks like this:
module.exports = {
pluginOptions: {
electronBuilder: {
nodeIntegration: true,
},
},
};
You can then use the snippets in Scenario 1 and Scenario 2.
Let's consider the following example where I assign a value using 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>
Here, the asynchronous call works as expected, my test data updates properly and shows in my view as soon as the Axios call got an answer.
Now, if I use the same logic to get my data from a method of the systeminformation library:
<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>
In that case, my view won't show the test data as Vue seems to hang on the systeminformation call indefinitely.
The console log won't even show as the await statement appears to make the mounted hook stuck.
I just tested systeminformation.getStaticData() function in my own Electron + Vue app and here's results:
It executes code asynchorously but the operation is so heavy, it makes the app almost completely unresponsive. It repeatedly spawns ~30 Node child processes effectively blocking 3-4 CPU threads.
I think it might be a bug with getStaticData() function. You might want to create a new issue in their repo and report it.
Either don't use this particular function and get all the needed info by running other systeminformation functions.
OR
Enable node integration in workers:
webPreferences: {
nodeIntegration: true,
nodeIntegrationInWorker: true
}
Execute this function in a worker, rather than on the main thread, and send the results back to your component, then terminate the worker. Though when you do this, open task manager and make sure it terminates all the spawned child processes with it:
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 })
}
}
If the worker throws errors, try adding the following to your vue.config:
module.exports = {
configureWebpack: {
module: {
rules: [
{
test: /\.worker\.js$/,
use: { loader: 'worker-loader' }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With