我记得在刚使用elecctron-vue
进行开发的时候是可以绑定的,通过一些事件触发也可以变更视图。于是重新构建了一个应用,当我没有引入主进程
和渲染进程
时,发现一切都是好的。然而添加过主进程
和渲染进程
通信后,发现在进程中无法使用this
进行绑定。
在上一篇在中发现了electron-vue
中的this
指向出现问题,在主进程
和渲染进程
之间通信的时候,尝试了不管使用何种方式,在ipRenderer
中this
永远指向窗口即electron
,而不是指向我们的Vue
。
更正一个问题,昨天说到我在登陆页面也无法通过this
绑定,发现是错误的,只有在进程中才无法通过this绑定数据到我们的视图中,也就是说在进程中通过this去改变vue-data中的数据,是无法去改变的,也是无法变更视图的
。而在其他位置(非进程通信中)是完全可以通过this
去变更视图。
这就会很蛋疼,也就是说在进程里面我用不了this
了?那我在进程中通过this
绑定数据的时候也会出现问题,我所有绑定的数据都绑定到了EventEmitter
这个对象中了。
在函数中我尝试对各处的this
进行打印输出,得到的结果如下
created() {
console.log("vue this:",this)
this.$electron.ipcRenderer.on("start-exe", function (event, result) {
console.log("进程通信中this:",this)
if (result && result.type === "err") {
notification.error({
message: "Notification Title",
description: result.msg,
});
} else if (result && result.type === "success") {
notification.success({
message: "Notification Title",
description: result.msg,
});
}
});
setTimeout(() => {
console.log('进程通信后this:', this)
}, 5000);
},
对于1的打印,可以看出
this
确实指向Vue
对于2的打印,可以看出this
却指向了node
的事假监听对象EventEmitter
对于3的打印,可以看出this
指向了Vue
这样,导致我们无法在通信的时候通过vue
去绑定变量或者属性,今天又调研了一下怎样改变this的指向?
,却发现实现不了。
后来又想了想既然进程通信间的数据不能绑定到vue
上,那能不能存起来呢?
最开始,我想到了vuex
去保存数据,但是想了想,应该不行。因为在使用vuex
的时候也会使用到this
,无法改变this指向,就无法去搞这个东西。
既然无法使用vuex
那只能使用其他的方式去保存进程中的东西。在进程通信的时候,可以将数据保存到本地或者窗口,比如使用window
对象或者localstorage
等去保存这些数据。
那保存的数据怎么去变更我们的视图呢?
目前想到的办法:
将渲染进程接收到的数据通过
window
保存下来,即window.resut = result
这种方式。然后在进程外使用定时器去给出个时间去控制,在一定时间后将window
中的值赋值给this.result
,那么就能去变更视图,就像下面的代码一样。但是这种方式的缺点就是定时的间隔时间无法确定。我曾调研进程的通信是不是异步的,按照常理来说应该是,因为他的输出结果会在同步任务之后输出,那异步任务应该会存放到js的事件队列中,而setTimeout
是宏事件,也会放到事件队列中,而且会在其他异步实践执行之后才会执行。
created() {
console.log("vue this:",this)
this.$electron.ipcRenderer.on("start-exe", function (event, result) {
console.log("进程通信中this:",this)
window.result = result
if (result && result.type === "err") {
notification.error({
message: "Notification Title",
description: result.msg,
});
} else if (result && result.type === "success") {
notification.success({
message: "Notification Title",
description: result.msg,
});
}
});
setTimeout(() => {
console.log('进程通信后this:', this)
this.result = window.result
}, 3000);
},
经测试,确实是可以变更视图,但这种方式并不是最好的,但也是目前能想到解决这种办法的。但是突然想到,以后一旦碰到主进程和渲染进程之间通信都要通过setTimeout
的方式去更新视图就会崩溃!!!
不知道你们有没有碰到这种问题,有没有什么更好的解决办法。如果有的话,欢迎留言!
在前几天做这个项目的时候,牵扯到主进程
和渲染进程
之间的通信的时候,如果想要获取到进程通信间的数据,尤其是在渲染进程
中监听主进程
传递的数据时,会出现this指向
的问题。
在偶然的一次修改代码时,发现有的地方是能用this去指向我们的vue,而不是窗口即EventEmitter
。
对比之下发现,原来并非是主进程
和渲染进程
之间的问题,而是我们在使用this
时,存在错误的用法。
我们知道全局this
默认指向的是window
,并且在使用的apply
或call
的时候也可以去改变this
的指向。
但是,我忽略了一点,那就是在es6+ 箭头函数也可以改变this的指向
。
于是便尝试在主进程
和渲染进程
通信时,将渲染进程
中的所涉及的函数全部使用箭头函数
来改变其this指向
。
为什么要在渲染进程中去做这件事情呢?
因为我们的渲染进程才是我们真正的vue项目,使用vue就牵扯到this
的问题。无论是页面变量,还是vue自带的$router
以及$store
都需要使用this
去绑定。所以我们主要的工作也就是在vue中去改变this指向
。
尝试一下修改
created() {
this.$electron.ipcRenderer.on("start-result", (event, result) => {
console.log("初始化this", this);
if (result && result.type === "err") {
notification.error({
message: "Notification Title",
description: result.msg,
});
} else if (result && result.type === "success") {
notification.success({
message: "Notification Title",
description: result.msg,
});
}
});
this.$electron.ipcRenderer.send("start-exe");
},
将之前的渲染进程中的生命周期函数中的进程监听中的函数改为箭头函数即将
function (event, result) {}
修改为(event, result) => {}
,然后查看一下打印输出。可以看到这次输出的确实是VueComponent
而不是之前的EventEmitter
,这样就可以在渲染进程中接受数据,并将数据绑定到this上,去改变我们的视图。
在之前的代码中,主进程
和渲染进程
的通信使用的是mainWindow
作为主进程为渲染进程发送消息,使用ipcRenderer
做渲染进程的监听和发送消息。
当时很笨拙的使用了setTimeout
去做延迟,以防止在返回数据的时候,没有接收到数据。这种方法是不可取的,谁都不能保证在通信的过程中时间的快慢,你就无法去准确的定位到需要延迟多长时间去赋值去获取数据。
然后就改用了ipcMain
和ipcRenderer
的方式去通信。这种方式还是很靠谱的。他不需要做延迟,什么时间获得数据了,然后将数据发送给渲染进程,在渲染进程中再将数据绑定到vue中的this中,从而更新到视图中
。
而且之前的方式是在主进程中通过执行exe的命令去启动子进程,子进程返回结果后,将子进程的数据通过setTimeout
的方式延迟发送给渲染进程。
看一下之前的主进程处理方式
const cp = require('child_process')
// 执行exe程序并获取输出值
function execPrograme() {
log.info("开始执行-----------------------------")
let message
let child = cp.spawn(`${__dirname}/main.exe`)
child.on('error',console.error)
child.stdout.on('data',(data)=>{
log.info('data=',data)
})
child.stderr.on('data',(data)=>{
log.info('data=',data)
})
// log.info(child.stdout)
setTimeout(()=>{
mainWindow.webContents.send('asynchronous-message',JSON.stringify(message))
},5000);
log.info("结束执行-----------------------------")
}
修改之后使用ipcMain
的方式处理主进程中的方式
import { ipcMain } from 'electron'
const cp = require('child_process')
let startMsg
// 启动agent exe
function execPrograme() {
log.info("开始执行-----------------------------")
let child = cp.spawn(`${__dirname}/main.exe`)
child.on('error', console.error)
child.stdout.on('data', (data) => {
let logs = data.toString().split('\n').filter(x => x);
logs.forEach(el => {
startMsg = `${el}\n\n`
});
})
child.stderr.on('data', (data) => {
let logs = data.toString().split('\n').filter(x => x);
logs.forEach(el => {
startMsg = `${el}\n\n`
});
})
log.info("结束执行-----------------------------")
}
// 监听渲染进程事件
ipcMain.on('start-exe', (event, arg)=>{
log.info('arg=', arg)
event.sender.send('start-result', JSON.parse(startMsg))
})
渲染进程中只要这么处理就行了
created() {
// 页面渲染后,渲染进程发送事件到主进程中
this.$electron.ipcRenderer.send("start-exe");
// 监听主进程事件,并获取返回数据,将数据绑定到this中
this.$electron.ipcRenderer.on("start-result", (event, result) => {
this.result = result
console.log("初始化this", this);
if (result && result.type === "err") {
notification.error({
message: "Notification Title",
description: result.msg,
});
} else if (result && result.type === "success") {
notification.success({
message: "Notification Title",
description: result.msg,
});
}
});
},
这样看起来结构就更清晰了
- 将监听事件放到之前的启动函数外面
- 当启动函数执行完,将子进程返回的数据保存到变量中
- 然后当页面加载到
created
方法时,渲染进程发送事件到主进程中- 主进程接收到渲染进程的请求,并将变量返回给渲染进程
- 渲染进程再去监听主进程的事件,将数据绑定到
vue的this中
。
至此就算解决了this
的指向问题,应该一开始就想到箭头函数this指向的问题
,做了这么久才发现这点,属实有点low了。
这样的话,ant.d
组件就可以正确的绑定到vue上,也就可以正常的使用message
和notification
等组件了。这部分代码还待修改,就不放上去了。按照官网给出的使用方式就可以了。