Electron 快速入门

2019-11-26 16:08栏目:龙电竞官网
TAG:

把它们想象成这样

Chrome(或其他浏览器)的每个标签页(tab)及其页面,就好比 Electron 中的一个单独渲染进程。即使关闭所有标签页,Chrome 依然存在。这好比 Electron 的主进程,能打开新的窗口或关闭这个应用。

注:在 Chrome 浏览器中,一个标签页(tab)中的页面(即除了浏览器本身部分,如搜索框、工具栏等)就是一个渲染进程。

图片 1

macOS

$ ./Electron.app/Contents/MacOS/Electron your-app/

Electron.app 里面是 Electron 发布包,你可以在 这里 下载到。

Brightray

Brightray是一个使libchromiumcontent更容易使用应用的静态库。它是专门为了Electron而创造的,但是也可以允许没有基于Electron的原生应用使用Chromium的渲染引擎。

Brightray是Electron的一个底层的依赖,大多数Electron的使用者并不用担心它。

如何在渲染进程调用原生弹框?

在渲染进程中调用原本专属于主进程中的 API (如弹框)的方式有两种:

  1. IPC 通讯模块:先在主进程通过 ipcMain 进行监听,然后在渲染进程通过 ipcRenderer 进行触发;
  2. remote 模块:该模块为渲染进程和主进程之间提供了快捷的通讯方式。

对于第二种方式,在渲染进程中,运行以下代码即可:

JavaScript

const remote = require('electron').remote remote.dialog.showMessageBox({ type: 'question', buttons: ['不告诉你', '没有梦想'], defaultId: 0, title: 'XCel', message: '你的梦想是什么?' }

1
2
3
4
5
6
7
8
9
10
const remote = require('electron').remote
 
remote.dialog.showMessageBox({
  type: 'question',
  buttons: ['不告诉你', '没有梦想'],
  defaultId: 0,
  title: 'XCel',
  message: '你的梦想是什么?'
}
 

Windows

$ .node_modules.binelectron .

NSIS

Nullsoft Scriptable Install System是一个Microsoft Windows下的脚本驱动的安装制作工具。它发布在免费软件许可证下,是一个类似于InstallShield的广泛的被用来替代商业专有产品的工具。electron-builder支持NSIS作为一个编译目标。

多进程!!!

前面说道,JavaScript 天生单线程,即使再快,对于数据量较大时,也会出现拒绝响应的问题。因此需要 Web Worker 或类似的方案去解决。

在这里我不选择 Web worker 的原因有如下几点:

  1. 有其它更好的替代方案:一个主进程能创建多个渲染进程,通过 IPC 即可进行数据交互;
  2. Electron 不支持 Web Worker!(当然,可能会在新版本支持,最新信息请关注官方)

Electron 作者在 2014.11.7 在《state of web worker support?》 issue 中回复了以下这一段:

Node integration doesn’t work in web workers, and there is no plan to do. Workers in Chromium are implemented by starting a new thread, and Node is not thread safe. Back in past we had tried to add node integration to web workers in Atom, but it crashed too easily so we gave up on it.

因此,我们最终采用了创建一个新的渲染进程 background process 进行处理数据。由 Electron 章节可知,每个 Electron 渲染进程是独立的,因此它们不会互相影响。但这也带来了一个问题:它们不能相互通讯?

错!下面有 3 种方式进行通讯:

  1. Storage API:对某个标签页的 localStorage/sessionStorage 对象进行增删改时,其他标签页能通过 window.storage 事件监听到。
  2. IndexedDB:IndexedDB 是一个为了能够在客户端存储可观数量的结构化数据,并且在这些数据上使用索引进行高性能检索的 API。
  3. 通过主进程作为中转站:设主界面的渲染进程是 A,background process 是 B,那么 A 先将 Excel 数据传递到主进程,然后主进程再转发到 B。B 处理完后再原路返回,具体如下图。当然,也可以将数据存储在主进程中,然后在多个渲染进程中使用 remote 模块来访问它。

该工具采用了第三种方式的第一种情况:
图片 2

1、主页面渲染进程 A 的代码如下:

JavaScript

//① ipcRenderer.send('filter-start', { filterTagList: this.filterTagList, filterWay: this.filterWay, curActiveSheetName: this.activeSheet.name }) // ⑥ 在某处接收 filter-response 事件 ipcRenderer.on("filter-response", (arg) => { // 得到处理数据 })

1
2
3
4
5
6
7
8
9
10
11
12
//①
ipcRenderer.send('filter-start', {
    filterTagList: this.filterTagList,
    filterWay: this.filterWay,
    curActiveSheetName: this.activeSheet.name
})
 
// ⑥ 在某处接收 filter-response 事件
ipcRenderer.on("filter-response", (arg) => {
    // 得到处理数据
})
 

2、作为中转站的主进程的代码如下:

JavaScript

//② ipcMain.on("filter-start", (event, arg) => { // webContents 用于渲染和控制 web page backgroundWindow.webContents.send("filter-start", arg) }) // ⑤ 用于接收返回事件 ipcMain.on("filter-response", (event, arg) => { mainWindow.webContents.send("filter-response", arg) })

1
2
3
4
5
6
7
8
9
10
11
//②
ipcMain.on("filter-start", (event, arg) => {
    // webContents 用于渲染和控制 web page
    backgroundWindow.webContents.send("filter-start", arg)
})
 
// ⑤ 用于接收返回事件
ipcMain.on("filter-response", (event, arg) => {
    mainWindow.webContents.send("filter-response", arg)
})
 

3、处理繁重数据的 background process 渲染进程 B 的代码如下:

JavaScript

// ③ ipcRenderer.on('filter-start', (event, arg) => { // 进行运算 ... // ④ 运算完毕后,再通过 IPC 原路返回。主进程和渲染进程 A 也要建立相应的监听事件 ipcRenderer.send('filter-response', { filRow: tempFilRow }) })

1
2
3
4
5
6
7
8
9
10
11
// ③
ipcRenderer.on('filter-start', (event, arg) => {
    // 进行运算
    ...
 
    // ④ 运算完毕后,再通过 IPC 原路返回。主进程和渲染进程 A 也要建立相应的监听事件
    ipcRenderer.send('filter-response', {
        filRow: tempFilRow
    })
})
 

至此,我们将『读取文件』、『过滤数据』和『导出文件』三大耗时的数据操作均转移到了 background process 中处理。

这里,我们只创建了一个 background process,如果想要做得更极致,我们可以新建『CPU 线程数- 1 』 个的 background process 同时对数据进行处理,然后在主进程对处理后数据进行拼接,最后再将拼接后的数据返回到主页面的渲染进程。这样就可以充分榨干 CPU 了。当然,在此笔者不会进行这个优化。

不要为了优化而优化,否则得不偿失。 —— 某网友

主进程与渲染进程的区别

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

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

由于在页面里管理原生 GUI 资源是非常危险而且容易造成资源泄露,所以在页面调用 GUI 相关的 APIs 是不被允许的。如果你想在网页里使用 GUI 操作,其对应的渲染进程必须与主进程进行通讯,请求主进程进行相关的 GUI 操作。

在 Electron,我们提供几种方法用于主进程和渲染进程之间的通讯。像 ipcRendereripcMain 模块用于发送消息, remote 模块用于 RPC 方式通讯。这些内容都可以在一个 FAQ 中查看 how to share data between web pages。

webview

webview标签是被用来在你的Electron应用中嵌入“guest”(例如一个外部网页)内容。他们是非常相似的内嵌框架,但是不同之处在于每一个webview运行在一个指定的进程中。它并没有和你的网页拥有相同的权限,并且在你的应用和嵌入内容之间交互都是异步的。这将保持你的应用对于嵌入内容的安全性。

Electron 是什么?

Electron 是一个可以用 JavaScript、HTML 和 CSS 构建桌面应用程序的。这些应用程序能打包到 Mac、Windows 和 Linux 系统上运行,也能上架到 Mac 和 Windows 的 App Store。

  • JavaScript、HTML 和 CSS 都是 Web 语言,它们是组成网站的一部分,浏览器(如 Chrome)懂得如何将这些代码转为可视化图像。
  • Electron 是一个库:Electron 对底层代码进行抽象和封装,让开发者能在此之上构建项目。

Windows

$ .electronelectron.exe your-app

main process

主进程,通常是一个叫做main.js的文件,是指向每一个Electron应用的入口。它控制着应用从打开到关闭的生命周期。它也管理着原生控件,比如MenuMenu BarDockTray等。主进程在应用中承担着创建每一个新的渲染进程的责任。全部的Node接口都在它里面。

每一个应用的主线程文件是在package.json文件中的main属性中被指定的。这是electron .如何知道启动时要执行哪个文件的原因。

参见:process,renderer process

XCel 项目总结:Electron 与 Vue 的性能优化

2017/03/01 · 基础技术 · Javascript, 算法

本文作者: 伯乐在线 - 刘健超-J.c 。未经作者许可,禁止转载!
欢迎加入伯乐在线 专栏作者。

XCEL 是由京东用户体验设计部凹凸实验室推出的一个 Excel 数据清洗工具,其通过可视化的方式让用户轻松地对 Excel 数据进行筛选。

XCEL 基于 Electron 和 Vue 2.x,它不仅跨平台(windows 7+、Mac 和 Linux),而且充分利用 Electron 多进程任务处理等功能,使其性能优异。

落地页: ✨✨✨
项目地址: ✨✨✨

手工下载 Electron 二进制文件

如果你手工下载了 Electron 的二进制文件,你也可以直接使用其中的二进制文件直接运行你的应用。

process

一个进程是一个正在运行的计算机程序的实例。Electron应用实际上是利用主进程和一个或几个渲染进程同时运行几个程序。

Node.js和Electron中,每一个运行着的进程都是一个process对象。这个对象是一个全局的并提供关于当前进程的信息和控制。作为一个全局的,它在应用中不使用require()也是有效的。

参见:main process, renderer process

CSS、JavaScript 和 Electron 相关的知识和技巧

electron-prebuilt

electron 是一个 npm 模块,包含所使用的 Electron 预编译版本。
如果你已经用 npm 全局安装了它,你只需要按照如下方式直接运行你的应用:

electron .

如果你是局部安装,那运行:

这个页面定义了一些在Electron中经常使用的专有名词。

内存占有量过大

解决了执行效率和渲染问题后,发现也存在内存占用量过大的问题。当时猜测是以下几个原因:

  1. 三大耗时操作均放置在 background process 处理。在通讯传递数据的过程中,由于不是共享内存(因为 IPC 是基于 Socket 的),导致出现多份数据副本(在写这篇文章时才有了这相对确切的答案)。
  2. Vuex 是以一个全局单例的模式进行管理,但它会是不是对数据做了某些封装,而导致性能的损耗呢?
  3. 由于 JavaScript 目前不具有主动回收资源的能力,所以只能主动对闲置对象设置为 null,然后等待 GC 回收。

由于 Chromium 采用多进程架构,因此会涉及到进程间通信问题。Browser 进程在启动 Render 进程的过程中会建立一个以 UNIX Socket 为基础的 IPC 通道。有了 IPC 通道之后,接下来 Browser 进程与 Render 进程就以消息的形式进行通信。我们将这种消息称为 IPC 消息,以区别于线程消息循环中的消息。 ——《Chromium的IPC消息发送、接收和分发机制分析》

定义:为了易于理解,以下『Excel 数据』均指 Excel 的全部有效单元格转为 JSON 格式后的数据。

最容易处理的无疑是第三点,手动将不再需要的变量及时设置为 null,但效果并不明显。

后来,通过操作系统的『活动监视器』(Windows 上是任务管理器)对该工具的每阶段(打开时、导入文件时、筛选时和导出时)进行粗略的内存分析,得到以下报告:

—————- S:报告分割线 —————- 经观察,主要耗内存的是页面渲染进程。下面通过截图说明:
PID 15243 是主进程
PID 15246 是页面渲染进程
PID 15248 是 background 渲染进程

a、首次启动程序时(第 4 行是主进程;第 1 行是页面渲染进程;第 3 行是 background 渲染进程 )

图片 3

b、导入文件(第 5 行是主进程;第 2 行是页面渲染进程;第 4 行是 background 渲染进程 )
图片 4

c、筛选数据(第 4 行是主进程;第 1 行是页面渲染进程;第 3 行是 background 渲染进程 )
图片 5

由于 JavaScript 目前不具有主动回收资源的功能,所以只能主动将对象设置为 null,然后等待 GC 回收。

因此,经过一段时间等待后,内存占用如下:
d、一段时间后(第 4 行是主进程;第 1 行是页面渲染进程;第 3 行是 background 渲染进程 ) 图片 6

由上述可得,页面渲染进程由于页面元素和 Vue 等 UI 相关资源是固定的,占用内存较大且不能回收。主进程占用资源也不能得到很好释放,暂时不知道原因,而 background 渲染进程则较好地释放资源。

—————- E:报告分割线 —————-

根据报告,初步得出的结论是 Vue 和通讯时占用资源较大。

根据该工具的实际应用场景:Excel 数据只在『导入』和『过滤后』两个阶段需要展示,而且展示的是通过 JavaScript 拼接的 HTML 字符串所构成的 DOM 而已。因此将表格数据放置在 Vuex 中,有点滥用资源的嫌疑。

另外,在 background process 中也有存有一份 Excel 数据副本。因此,索性只在 background process 存储一份 Excel 数据,然后每当数据变化时,通过 IPC 让 background process 返回拼接好的 HTML 字符串即可。这样一来,内存占有量立刻下降许多。另外,这也是一个一举多得的优化:

  1. 字符串拼接操作也转移到了 background process,页面渲染进程进一步减少耗时的操作;
  2. 内存占有量大大减小,响应速度也得到了提升。

其实,这也有点像 Vuex 的『全局单例模式管理』,一份数据就好。

当然,对于 Excel 的基本信息,如行列数、SheetName、标题组等均依然保存在 Vuex。

优化后的内存占有量如下图。与上述报告的第三张图相比(同一阶段),内存占有量下降了 44.419%: 图片 7
另外,对于不需要响应的数据,可通过 Object.freeze() 冻结起来。这也是一种优化手段。但该工具目前并没有应用到。

至此,优化部分也阐述完毕了!


该工具目前是开源的,欢迎大家使用或推荐给用研组等有需要的人。

你们的反馈(可提交 issues / pull request)能让这个工具在使用和功能上不断完善。

最后,感谢 LV 在产品规划、界面设计和优化上的强力支持。全文完!

打赏支持我写出更多好文章,谢谢!

打赏作者

快速入门

Electron 可以让你使用纯 JavaScript 调用丰富的原生 APIs 来创造桌面应用。你可以把它看作一个专注于桌面应用的 Node.js 的变体,而不是 Web 服务器。

这不意味着 Electron 是绑定了 GUI 库的 JavaScript。相反,Electron 使用 web 页面作为它的 GUI,所以你能把它看作成一个被 JavaScript 控制的,精简版的 Chromium 浏览器。

ASAR

ASAR是Atom Shell Archive Format的简称。一个asar文档是一个把文件都放在一个单独的文件中的简单的tar-like类型文件。Electron可以从中读取全部的文件而不用解压整个文件。

创造ASAR类型主要是为了在Windows下提高性能... TODO

Excel 的列转换

  • Excel 的列需要用『字母』表示,但不能简单地通过 String.fromCharCode() 实现,因为当超出 26 列 时就会产生问题(如:第 27 列,String.fromCharCode(65+26) 得到的是 [,而不是 AA)。因此,这需要通过『十进制和 26 进制转换』算法来实现。

JavaScript

// 将传入的自然数转换为26进制表示。映射关系:[0-25] -> [A-Z]。 function getCharCol(n) { let temCol = '', s = '', m = 0 while (n >= 0) { m = n % 26 + 1 s = String.fromCharCode(m + 64) + s n = (n - m) / 26 } return s }

1
2
3
4
5
6
7
8
9
10
11
12
13
// 将传入的自然数转换为26进制表示。映射关系:[0-25] -> [A-Z]。
function getCharCol(n) {
  let temCol = '',
    s = '',
    m = 0
  while (n >= 0) {
    m = n % 26 + 1
    s = String.fromCharCode(m + 64) + s
    n = (n - m) / 26
  }
  return s
}
 

JavaScript

// 将传入的26进制转换为自然数。映射关系:[A-Z] ->[0-25]。 function getNumCol(s) { if (!s) return 0 let n = 0 for (let i = s.length

  • 1, j = 1; i >= 0; i--, j *= 26) { let c = s[i].toUpperCase() if (c < 'A' || c > 'Z') return 0 n += (c.charCodeAt() - 64) * j } return n - 1 }
1
2
3
4
5
6
7
8
9
10
11
12
// 将传入的26进制转换为自然数。映射关系:[A-Z] -&gt;[0-25]。
function getNumCol(s) {
  if (!s) return 0
  let n = 0
  for (let i = s.length - 1, j = 1; i &gt;= 0; i--, j *= 26) {
    let c = s[i].toUpperCase()
    if (c &lt; 'A' || c &gt; 'Z') return 0
    n += (c.charCodeAt() - 64) * j
  }
  return n - 1
}
 

参照下面例子

复制并且运行这个库 electron/electron-quick-start

注意:运行时需要你的系统已经安装了 Git 和 Node.js(包含 npm)。

# 克隆这仓库
$ git clone https://github.com/electron/electron-quick-start
# 进入仓库
$ cd electron-quick-start
# 安装依赖库并运行应用
$ npm install && npm start

更多 apps 例子,查看 electron 社区创建的 list of boilerplates。

Squirrel

Squirrel是一个开源的框架,可以允许Electron应用自动升级到已经发布的最新版本。查看autoUpdater接口的使用Squirrel启动的信息。

开发体验如何?

基于 Electron 的开发就像在开发网页,而且能够无缝地 使用 Node。或者说:在构建一个 Node 应用的同时,通过 HTML 和 CSS 构建界面。另外,你只需为一个浏览器(最新的 Chrome)进行设计(即无需考虑兼容性等)。

  • 使用 Node:这还不是全部!除了完整的 Node API,你还可以使用托管在 npm 上超过 350,000 个的模块。
  • 一个浏览器:并非所有浏览器都提供一致的样式,Web 设计师和开发者经常因此而不得不花费更多的精力,让网站在不同浏览器上表现一致。
  • 最新的 Chrome:可使用超过 90% 的 ES2015 特性和其它很酷的特性(如 CSS 变量)。

Linux

$ ./electron/electron your-app/

原文:https://github.com/electron/electron/blob/master/docs/glossary.md
译者:Lin

思路与实现

基于用研组的需求,利用 Electron 和 Vue 的特性对该工具进行开发。

主进程

在 Electron 里,运行 package.jsonmain 脚本的进程被称为主进程。在主进程运行的脚本可以以创建 web 页面的形式展示 GUI。

V8

V8是Google的开源JavaScrip引擎。它是用C++编写的并且被用在Google Chrome中,Chrome是Google的开源浏览器。V8可以单独运行,或者被嵌入到任何C++应用中。

为 Electron 应用生成 Windows 安装包

通过 electron-builder 可直接生成常见的 MacOS 安装包,但它生成的 Windows 的安装包却略显简洁(默认选项时)。

图片 8
Mac 常见的安装模式,将“左侧的应用图标”拖拽到“右侧的 Applications”即可

通过 electron-builder 生成的 Windows 安装包与我们在 Windows 上常见的软件安装界面不太一样,它没有安装向导和点击“下一步”的按钮,只有一个安装时的 gif 动画(默认的 gif 动画如下图,当然你也可以指定特定的 gif 动画),因此也就关闭了用户选择安装路径等权利。

图片 9
Windows 安装时 默认显示的 gif 动画

如果你想为打包后的 Electron 应用(即通过 electron-packager/electron-builder 生成的,可直接运行的程序目录)生成拥有点击“下一步”按钮和可让用户指定安装路径的常见安装包,可以尝试 NSIS 程序,具体可看这篇教程 《[教學]只要10分鐘學會使用 NSIS 包裝您的桌面軟體–安裝程式打包。完全免費。》。

注:electron-builder 也提供了生成安装包的配置项,具体查看>>。

NSIS(Nullsoft Scriptable Install System)是一个开源的 Windows 系统下安装程序制作程序。它提供了安装、卸载、系统设置、文件解压缩等功能。正如其名字所描述的那样,NSIS 是通过它的脚本语言来描述安装程序的行为和逻辑的。NSIS 的脚本语言和常见的编程语言有类似的结构和语法,但它是为安装程序这类应用所设计的。

至此,CSS、JavaScript 和 Electron 相关的知识和技巧部分阐述完毕。


macOS / Linux

$ ./node_modules/.bin/electron .

userland

这个术语来自于Unix社区,"userland"或"userspace"在运行在操作系统内核之外的程序中被提及。最近,这个术语已经在Node和npm社区中普及,用于区分"Node core"和npm上记录的通过更大的"user"社区发布的包。

像Node,Electron是一个专注于有一个小的接口集合,并且这个集合提供所有的必须的为了开发多平台桌面程序的原生接口。这个设计理念使得Electron保持为一个灵活的工具,而不是过多的规定如何来使用它。Userland使得用户可以创建并分享工具,而这些工具提供基于“core”中有效内容之上的附加功能。

Vue 全家桶

该工具使用了 Vue、Vuex、Vuex-router。在工具基本定型阶段,由 1.x 升级到了 2.x。

运行你的应用

一旦你创建了最初的 main.jsindex.htmlpackage.json 这几个文件,你可能会想尝试在本地运行并测试,看看是不是和期望的那样正常运行。

native modules

Native modules(在Node.js中也叫插件)是C或C++写的模块,使用require()函数可以被加载到Node.js或Electron中,然后就可以像一个普通Node.js模块一样使用了。它们主要用来提供一个把js运行在Node.js和C/C++库上的接口。

Electron支持Native Node modules,但是由于Electron非常有可能使用安装在你电脑上的Node二进制文件中的不同版本的V8,你在编译native modules的时候需要手动指定Electron的头部位置。

参考Using Native Node Modules。

更贴近原生应用

Electron 的一个缺点是:即使你的应用是一个简单的时钟,但它也不得不包含完整的基础设施(如 Chromium、Node 等)。因此,一般情况下,打包后的程序至少会达到几十兆(根据系统类型进行浮动)。当你的应用越复杂,就越可以忽略文件体积问题。

众所周知,页面的渲染难免会导致『白屏』,而且这里采用了 Vue 这类框架,情况就更加糟糕了。另外,Electron 应用也避免不了『先打开浏览器,再渲染页面』的步骤。下面提供几种方法来减轻这种情况,以让程序更贴近原生应用。

  1. 指定 BrowserWindow 的背景颜色;
  2. 先隐藏窗口,直到页面加载后再显示;
  3. 保存窗口的尺寸和位置,以让程序下次被打开时,依然保留的同样大小和出现在同样的位置上。

对于第一点,若应用的背景不是纯白(#fff)的,那么可指定窗口的背景颜色与其一致,以避免渲染后的突变。

JavaScript

mainWindow = new BrowserWindow({ title: 'XCel', backgroundColor: '#f5f5f5', };

1
2
3
4
5
mainWindow = new BrowserWindow({
    title: 'XCel',
    backgroundColor: '#f5f5f5',
};
 

对于第二点,由于 Electron 本质是一个浏览器,需要加载非网页部分的资源。因此,我们可以先隐藏窗口。

JavaScript

var mainWindow = new BrowserWindow({ title: 'ElectronApp', show: false, };

1
2
3
4
5
var mainWindow = new BrowserWindow({
    title: 'ElectronApp',
    show: false,
};
 

等到渲染进程开始渲染页面的那一刻,在 ready-to-show 的回调函数中显示窗口。

JavaScript

mainWindow.on('ready-to-show', function() { mainWindow.show(); mainWindow.focus(); });

1
2
3
4
5
mainWindow.on('ready-to-show', function() {
    mainWindow.show();
    mainWindow.focus();
});
 

对于第三点,笔者并没有实现,原因如下:

  1. 用户一般是根据当时的情况对程序的尺寸和位置进行调整,即视情况而定。
  2. 以上是我个人臆测,主要是我懒

    版权声明:本文由龙竞技官网发布于龙电竞官网,转载请注明出处:Electron 快速入门