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-main
electron-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; }