我做了第一次尝试,想使用角度渲染器进程中的电子api。我遵循中的说明
创建角电子应用的说明
所以在我的main.js文件中,我添加了:
const {app, BrowserWindow, ipcMain} = require('electron')
我还添加了
function openModal(){
const { BrowserWindow } = require('electron');
let modal = new BrowserWindow({ parent: mainWindow, modal: true, show: false })
modal.loadURL('https://www.sitepoint.com')
modal.once('ready-to-show', () => {
modal.show()
})
}
ipcMain.on('openModal', (event, arg) => {
openModal()
})
在我的app.component.ts文件中,添加了导入import { IpcRenderer } from 'electron';
我添加了以下构造函数
private ipc: IpcRenderer
constructor(){
if ((<any>window).require) {
try {
this.ipc = (<any>window).require('electron').ipcRenderer;
} catch (e) {
throw e;
}
} else {
console.warn('App not running inside Electron!');
}
}
由于我的CLI并不完全清楚icp将是我在这行中添加的IpcRenderer类型
private ipc: IpcRenderer | any;
具有功能
openModal(){
console.log("Open a modal");
this.ipc.send("openModal");
}
它应该能够向";主";过程但如果我调用函数,我会得到错误
TypeError:无法读取未定义的属性"send">
我做错了什么?
我也遇到过同样的问题,但发现了一个长时间的破解。
原因
看看这一行:
(<any>window).require
此行在全局窗口对象中搜索require()
方法,这是一个NodeJS函数,因此如果您使用Electron版本>5,因为BrowserWindow类的webPreferences -> nodeIntegration
属性默认设置为false。
解决方案
有两种解决方案:
- 设置
nodeIntegration: true
和contextIsolation: false
应用程序将按预期运行但此黑客攻击存在安全问题。如本答复所述:
Electron应用程序很棒,因为我们可以使用node,但这种能力是一把双刃剑。如果我们不小心,我们会通过我们的应用程序允许某人访问node,而使用node,一个糟糕的参与者可能会损坏您的机器或删除您的操作系统文件(我想还有其他事情(。
- 使用
contextBridge
和preload.js:这是从上面的答案和这个问题的其他答案中得出的
我将使用Angular提供一个解决方法。
在main.js中创建mainWindow
时,在webPreferences
中进行以下修改:
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
enableRemoteModule: false,
preload: path.join(__dirname, "preload.js")
}
在main.js
所在的同一目录中,创建一个preload.js
文件并在其中添加以下代码(您可以省略receive()
声明,因为在本用例中不需要它(。
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 = ["openModal"];
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
对象中注入window.api.send()
和window.api.receive()
方法。如果您尝试在角度组件中直接访问它们,linter将出错,因为它们没有在标准窗口对象上定义。为了解决这个问题,我们将创建一个Angular服务并在我们的应用程序中注入。此服务将引用在运行时创建的浏览器的窗口对象,并添加了以上两个函数。
创建一个WindowRefService
并添加以下代码:
窗口引用服务.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class WindowRefService {
getWindow(): any {
return window;
}
constructor() { }
get nativeWindow(): Window{
return this.getWindow();
}
}
不要忘记在AppModule中注册服务:
...
import { WindowRefService } from './window-ref.service';
...
@NgModule{
...
providers: [WindowRefService]
}
之后将其注入您的组件:
import { WindowRefService } from './window-ref.service';
...
private _window:any;
constructor(windowRef: WindowRefService){
this._window = windowRef.nativeWindow;
}
...
//openModal function
openModal(){
console.log("Open a modal");
this._window.api.send("openModal", /* data to be sent, if any*/);
}
删除构造函数中的private ipc: IpcRenderer | any;
和其他现有代码,因为这不是必需的。