Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

万物皆可快速上手之Electron(第一弹) #62

Open
Cosen95 opened this issue Nov 16, 2020 · 0 comments
Open

万物皆可快速上手之Electron(第一弹) #62

Cosen95 opened this issue Nov 16, 2020 · 0 comments

Comments

@Cosen95
Copy link
Owner

Cosen95 commented Nov 16, 2020

最近在开发一款桌面端应用,用到了ElectronReact

React作为日常使用比较频繁的框架,这里就不详细说明了,这里主要是想通过几篇文章让大家快速上手Electron以及与React完美融合。

本篇是系列文章的第一篇,主要是给大家分享Electron的一些概念,让大家对Electron有一个初步的认知。

先来了解一下什么是Electron吧,可能很多小伙伴还没有听过Electron,相信很多小伙伴此时的表情是这样的:

看下官网的自我介绍:

Electron 是一个可以使用 Web 技术如 JavaScriptHTMLCSS 来创建跨平台原生桌面应用的框架。借助 Electron,我们可以使用纯 JavaScript 来调用丰富的原生 APIs

Electronweb 页面作为它的 GUI,而不是绑定了 GUI 库的 JavaScript。它结合了 ChromiumNode.js 和用于调用操作系统本地功能的 APIs(如打开文件窗口、通知、图标等)。

上面这张图很好的说明了Electron的强大之处。

正因如此,现在已经有很多由Electron开发的应用,比如AtomVisual Studio Code等。我们可以在Apps Built on Electron看到所有由Electron构建的项目。

快速开始

前面说了那么多废话,下面进入正题,带大家用五分钟(为什么是五分钟?我猜的 🐶 )的时间运行一个ElectronHello World

安装

这一步很简单:

npm install electron -g

第一个 Electron 应用

一个最简单的 Electron 应用目录结构如下:

hello-world/
├── package.json
├── main.js
└── index.html

package.json的格式和 Node 的完全一致,并且那个被 main 字段声明的脚本文件是你的应用的启动脚本,它运行在主进程上。你应用里的 package.json 看起来应该像:

{
  "name": "hello-world",
  "version": "0.1.0",
  "main": "main.js"
}

创建main.js文件并添加如下代码:

const { app, BrowserWindow } = require("electron");
const isDev = require("electron-is-dev");
const path = require("path");
let mainWindow;

app.on("ready", () => {
  mainWindow = new BrowserWindow({
    width: 1024,
    height: 680,
    webPreferences: {
      nodeIntegration: true,
      // https://stackoverflow.com/questions/37884130/electron-remote-is-undefined
      enableRemoteModule: true,
    },
  });
  // https://www.electronjs.org/docs/api/browser-window#event-ready-to-show
  // 在加载页面时,渲染进程第一次完成绘制时,如果窗口还没有被显示,渲染进程会发出 ready-to-show 事件 。 在此事件后显示窗口将没有视觉闪烁
  mainWindow.once("ready-to-show", () => {
    mainWindow.show();
  });
  const urlLocation = `file://${__dirname}/index.html`;
  mainWindow.loadURL(urlLocation);
});

然后是index.html文件:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Hello World!</title>
    <style media="screen">
      .version {
        color: red;
      }
    </style>
  </head>
  <body>
    <h1>Hi! 我是柯森!</h1>
  </body>
</html>

到这里main.jsindex.htmlpackage.json 这几个文件都有了。万事俱备,来运行这个项目。因为前面已经全局安装了electron,所以我们可以使用 electron 命令来运行项目。在 hello-world/ 目录里面运行下面的命令:

$ electron .

你会发现会弹出一个 electron 应用客户端,如图所示:

到这里,我们已经完成了一个最简单的electron 应用。

但你一定会对上面用到的一些api有疑惑,下面我将带大家深入浅出的了解一下electron的常用概念和api

相关概念

Electron 的进程分为主进程和渲染进程。在说这个之前,我觉得有必要先说一下进程和线程的概念。

进程和线程

这里参考的是廖雪峰老师关于进程和线程概念的阐述,我觉得说的清晰明了。

对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开一个记事本就启动了一个记事本进程,打开两个记事本就启动了两个记事本进程,打开一个Word就启动了一个Word进程。

有些进程还不止同时干一件事,比如Word,它可以同时进行打字、拼写检查、打印等事情。在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread)。

主进程和渲染进程

主进程

electron 里面,运行 package.json 里面 main 脚本的进程被称为主进程。主进程控制整个应用的生命周期,在主进程中可以创建 Web 形式的 GUI,而且整个 Node API 是内置其中。

渲染进程

由于 Electron 使用 Chromium 来展示页面,所以 Chromium 的多进程架构也被充分利用。每个 Electron 的页面都在运行着自己的进程,这样的进程我们称之为渲染进程

在一般浏览器中,网页通常会在沙盒环境下运行,并且不允许访问原生资源。然而,Electron 用户拥有与底层操作系统直接交互的能力。

主进程与渲染进程的区别

主进程使用BrowserWindow实例创建页面。每个BrowserWindow实例都在自己的渲染进程里运行页面。当一个BrowserWindow实例被销毁后,相应的渲染进程也会被终止。

主进程管理所有页面和与之对应的渲染进程。每个渲染进程都是相互独立的,并且只关心他们自己的页面。

electron 中,页面不直接调用底层 APIs,而是通过主进程进行调用。所以如果你想在网页里使用 GUI 操作,其对应的渲染进程必须与主进程进行通讯,请求主进程进行相关的 GUI 操作。

electron 中,主进程和渲染进程的通信主要有以下几种方式:

  • ipcMain、ipcRender
  • Remote 模块

进程通信将稍后在下文详细介绍。

BrowserWindow 的创建

BrowserWindow用于创建和控制浏览器窗口。像上面的hello-world中:

mainWindow = new BrowserWindow({
  width: 1024,
  height: 680,
  webPreferences: {
    nodeIntegration: true,
    // https://stackoverflow.com/questions/37884130/electron-remote-is-undefined
    enableRemoteModule: true,
  },
});

const urlLocation = `file://${__dirname}/index.html`;
mainWindow.loadURL(urlLocation);

创建了一个1024*680的窗口,并通过loadURL方法来加载了一个本地的html文件。

这里一般会通过区分环境加载对应不同的文件。

进程间的通信

在计算机系统设计中,不同的进程间内存资源都是相互隔离的,因此进程间的数据交换,会使用进程间通讯方式达成。而不同于一般的原生应用开发,Electron 的渲染进程与主进程分别属于独立的进程中,而且进程间会存在频繁的数据交换,这时选择一个合理的进程间通讯方式显得尤为重要。下面是 Electron 中官方提供的进程间通讯方式:

window.postMessage,LocalStorage

在前端开发中,鉴于浏览器对本地数据有严格的访问限制,所以一般通过该两种方式进行窗口间的数据通讯,该方式同样适用于 Electron 开发中。然而因为 API 设计目的仅仅是为了前端窗口间简单的数据传输,大量以及频繁的数据通讯会导致应用结构松散,同时传输效率也值得怀疑。

使用IPC进行通信

Electron 中提供了 ipcRenderipcMain 作为主进程以及渲染进程间通讯的桥梁,该方式属于 Electron 特有传输方式,不适用于其他前端开发场景。Electron 沿用 Chromium 中的 IPC 方式,不同于 sockethttp 等通讯方式,Chromium 使用的是命名管道 IPC ,能够提供更高的效率以及安全性。

主进程收发信息

详细参考ipcMain

  • 主进程接收渲染进程发送的信息
ipcMain.on("message", (e, msg) => {
  console.log(msg);
});
  • 主进程(主窗口)发送信息给渲染进程
mainWindow.webContents.send('message', { name: 'from the main by cosen' });

渲染进程收发信息

通过ipcRenderer发送或接收

  • 渲染进程接收主进程发送的信息
ipcRenderer.on("message", (e, msg) => {
  console.log(msg);
});
  • 渲染进程发送信息给主进程
ipcRenderer.send("message", { name: "Cosen" });

使用remote实现跨进程访问

remote 模块提供了一种在渲染进程(网页)和主进程之间进行进程间通讯(IPC)的简便途径。

Electron中, 与GUI相关的模块(如 dialog, menu 等)只存在于主进程,而不在渲染进程中 。为了能从渲染进程中使用它们,需要用ipc模块来给主进程发送进程间消息。使用 remote 模块,可以调用主进程对象的方法,而无需显式地发送进程间消息。

总结

本小节我们大概的了解了Electron的一些概念以及运行了一个入门的hello-world程序。但这远远还不够,下一节我会讲一下如何将ElectronReact完美融合,毕竟还是要更贴近业务的~

好了,不早了,我要去开启我的网易云时光了 🤖

❤️ 爱心三连击

1.如果觉得这篇文章还不错,来个分享、点赞、在看三连吧,让更多的人也看到~

2.关注公众号前端森林,定期为你推送新鲜干货好文。

3.特殊阶段,带好口罩,做好个人防护。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant