electron && angular 构建桌面应用(二)

electronvolt 2020-02-12

electron如何工作?

Electron由主进程和渲染器进程所组成。每个进程在应用程序中扮演不同的角色,Electron包含不同的模块来帮助您构建应用程序。某些模块,例如从系统剪贴板读写的能力,在这两种类型的进程中都可用。其他的,比如访问操作系统接口的能力,仅限于主进程。

主进程 main process

Electron运行packagemain脚本的进程被称为主进程主流程有几个重要的职责。它可以响应应用程序生命周期事件,例如启动、退出、准备退出、后台调用、前台跳转等等。主过程也负责与本机操作系统接口通信。如果你想要显示对话框去打开或者保存文件,可以从主进程中执行。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;
}

相关推荐