electron如何工作?
Electron由主進程和渲染器進程所組成。每個進程在應用程序中扮演不同的角色,Electron包含不同的模塊來幫助您構建應用程序。某些模塊,例如從系統剪貼板讀寫的能力,在這兩種類型的進程中都可用。其他的,比如訪問操作系統接口的能力,僅限於主進程。
主進程 main process
Electron運行package的main腳本的進程被稱為主進程。主流程有幾個重要的職責。它可以響應應用程序生命周期事件,例如啟動、退出、准備退出、后台調用、前台跳轉等等。主過程也負責與本機操作系統接口通信。如果你想要顯示對話框去打開或者保存文件,可以從主進程中執行。package.json里的main配置的js文件,即為主進程。
渲染進程 renderer process
主進程可以使用Electron瀏覽器窗口模塊創建和銷毀渲染器進程,渲染器進程可以加載web頁面來顯示用戶界面。每一個進程利用Chromium的多進程架構,並在自己的線程上運行,然后,這些頁面可以加載其他JavaScript文件並在此進程中執行代碼。與普通web頁面不同,你可以在自己的渲染進程中,訪問所有Node APIs,允許使用本機模塊和較低級別的系統交互。每個渲染進程都是獨立的,無法訪問操作系統集成的接口。如果需要觸發打開或保存文件對話框或訪問任何其他操作系統集成,其對應的渲染進程必須與主進程進行通訊。
使用electron的api
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();
}
}
使用Nodejs的api
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主線程webpack的target配置項為
electron-main - 修改electron渲染線程的webpack的target配置項為
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;
}
