electronvolt 2020-02-12
Electron由主进程和渲染器进程所组成。每个进程在应用程序中扮演不同的角色,Electron包含不同的模块来帮助您构建应用程序。某些模块,例如从系统剪贴板读写的能力,在这两种类型的进程中都可用。其他的,比如访问操作系统接口的能力,仅限于主进程。
Electron运行package的main脚本的进程被称为主进程。主流程有几个重要的职责。它可以响应应用程序生命周期事件,例如启动、退出、准备退出、后台调用、前台跳转等等。主过程也负责与本机操作系统接口通信。如果你想要显示对话框去打开或者保存文件,可以从主进程中执行。package.json里的main配置的js文件,即为主进程。
主进程可以使用Electron浏览器窗口模块创建和销毁渲染器进程,渲染器进程可以加载web页面来显示用户界面。每一个进程利用Chromium的多进程架构,并在自己的线程上运行,然后,这些页面可以加载其他JavaScript文件并在此进程中执行代码。与普通web页面不同,你可以在自己的渲染进程中,访问所有Node APIs,允许使用本机模块和较低级别的系统交互。每个渲染进程都是独立的,无法访问操作系统集成的接口。如果需要触发打开或保存文件对话框或访问任何其他操作系统集成,其对应的渲染进程必须与主进程进行通讯。
Electron在主进程和渲染进程中提供了大量API去帮助开发桌面应用程序, 在主进程和渲染进程中,你可以通过require的方式将其包含在模块中以此,获取Electron的API。
import { app, BrowserWindow, screen, ipcMain } from ‘electron‘;所有Electron的API都被指派给一种进程类型。 许多API只能被用于主进程或渲染进程中,但其中一些API可以同时在上述两种进程中使用。 每一个API的文档都将声明你可以在哪种进程中使用该API。
Electron中的窗口是使用BrowserWindow类型创建的一个实例, 它只能在主进程中使用。
// 这样写在主进程会有用,但是在渲染进程中会提示‘未定义‘
const { BrowserWindow } = require(‘electron‘)
const win = new BrowserWindow()因为进程之间的通信是被允许的, 所以渲染进程可以调用主进程来执行任务。 Electron通过remote模块暴露一些通常只能在主进程中获取到的API。 为了在渲染进程中创建一个BrowserWindow的实例,我们通常使用remote模块为中间件:
import { remote, ipcRenderer } from ‘electron‘;
@Component({
selector: ‘app-root‘,
templateUrl: ‘./app.component.html‘,
styleUrls: [‘./app.component.scss‘]
})
export class AppComponent{
remote: typeof remote;
fs: typeof fs;
ipcRender: typeof ipcRenderer;
constructor(){
this.remote = window.require(‘electron‘).remote;
const win = this.remote.BrowserWindow();
}
}Electron同时对主进程和渲染进程暴露了Node.js 所有的接口。 这里有两个重要的定义:
1)所有在Node.js可以使用的API,在Electron中同样可以使用。 在Electron中调用如下代码是有用的:
import * as path from ‘path‘;
@Component({
selector: ‘app-root‘,
templateUrl: ‘./app.component.html‘,
styleUrls: [‘./app.component.scss‘]
})
export class AppComponent{
fs;
constructor(){
this.fs = window.require(‘fs‘);
console.log("fs:", this.fs.readdirSync(‘/‘));
console.log("path:", path.join(__dirname, ‘dist/index.html‘));
}
}坑1:渲染进程使用nodejs的api时,如fs,需要通过var fs = window.require(‘fs)这种方式引入。写成var fs = require(‘fs‘) 或 import * as fs from ‘fs‘ 时会报找不到fs module的错误。还可以通过配置webpack的配置项来解决该问题。
更佳的解决方案是通过webpack自身来帮我们解决,即修改webpack提供的target产出目标为electron,即:
electron-mainelectron-renderer这样就可以更好的解决上面的问题。
坑2:electron默认在渲染进程中不支持使用nodejs。需要在主进程中进行以下配置:
win = new BrowserWindow({
width: 500,
height: 800,
webPreferences: {
nodeIntegration: true,
},
});需将nodeIntegration置为true。
渲染进程中使用ipcRenderer.
import { Component } from ‘@angular/core‘;
import { remote, ipcRenderer } from ‘electron‘;
@Component({
selector: ‘app-root‘,
templateUrl: ‘./app.component.html‘,
styleUrls: [‘./app.component.scss‘]
})
export class AppComponent{
remote: typeof remote;
ipcRender: typeof ipcRenderer;
constructor(){
this.remote = window.require(‘electron‘).remote;
this.ipcRender = window.require(‘electron‘).ipcRenderer;
}
sendMsg(){
this.ipcRender.send(‘create-folder‘);
this.ipcRender.on(‘create-folder-result‘, function(event, data) {
console.log(data);//finished!
})
}
}主进程使用ipaMain
import { app, BrowserWindow, screen, ipcMain } from ‘electron‘;
let win: BrowserWindow = null;
function createWindow(): BrowserWindow {
// Create the browser window.
win = new BrowserWindow({
width: 500,
height: 800,
webPreferences: {
nodeIntegration: true,
},
});
win.loadURL(‘http://localhost:4200‘);
//win.loadURL(url.format({
// pathname: path.join(__dirname, ‘dist/index.html‘),
// protocol: ‘file:‘,
// slashes: true
//}));
return win;
}
try {
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on(‘ready‘, createWindow);
// Quit when all windows are closed.
app.on(‘window-all-closed‘, () => {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== ‘darwin‘) {
app.quit();
}
});
app.on(‘activate‘, () => {
// On OS X it‘s common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (win === null) {
createWindow();
}
});
ipcMain.on(‘create-folder‘, (evt, arg) => {
console.log("ipcMain");
evt.sender.send(‘create-folder-result‘, { msg: ‘finished!‘ })
})
} catch (e) {
// Catch Error
// throw e;
}