electron 初步学习

原理

Electron通过将Chromium和Node.js合并到同⼀个运⾏时环境中,并将其打包为Mac, Windows和Linux系统下的应⽤来实现这⼀⽬的。

Electron本质就是提供了一个浏览器的壳子,用于运行我们的web应用。

目前浏览器采用多线程架构:

其中:

  • 浏览器主进程,实现浏览器的主要UI、负责和文件、网络等等操作系统底层接口对接通信。

  • 渲染进程,每一个tab独立开一个渲染进程,核心进行web代码解析和渲染工作。

  • 插件进程,负责浏览器插件的控制。

  • GPU进程,独立的进程负责处理GPU图像的渲染绘制。

这样设计架构的好处主要有两个:

  1. 保证每个tab独立进程,这样在某一个页面crash的时候,仅仅影响当前的Tab,而不至于让整个浏览器崩溃。这提升了软件的健壮性和用户体验。

  2. 有利于实现沙箱隔离的安全机制,基于进程可以很方便地控制不同页面之间的安全访问策略,确保每个renderer进程在自己单独的沙箱环境内安全地运行。

Electron本身参考了这个架构的实现,将各个GUI窗口通过renderer进程实现,交由chromium来加载渲染,主进程集成Node.js,负责与系统API交互,处理核心事务。

技术集成

Electron是一个集成项目,允许开发者使用前端技术开发桌面端应用。它做了如下几个重要的工作:

  1. 定制 Chromium,并把定制版本的 Chromium 集成在 Electron 内部;

  2. 定制 Node.js,并把定制版本的 Node.js 集成在 Electron 内部;

  3. 通过消息轮询机智打通 Node.js 和 Chromium 的消息循环;

  4. 通过 electron 的内置模块向开发者提供桌面应用开发必备的API;

其中Chromium 基础能力可以让应用渲染HTML(CSS)页面,可以执行页面的JavaScript脚 本,让应用可以在Cookie、LocalStorage或IndexedDB 中存取数据。除此之外,Electron还允许 开发者突破同源策略的限制:伪装请求,截获响应,修改session等。

Node.js 基础能力可以让开发者读写本地磁盘的文件、通过socket访问网络、创建和控制子进程等。除此之外,还修改了Node的加解密机制让Chromium的BoringSSL和Node的OpenSSL兼容的 更好,让Node.js可以加载asar压缩包内的文件等。

Electron 内置模块可以让开发者创建操作系统的托盘图标、访问操作系统的剪切板、屏幕信息、发送系统通知等,除此之外还提供了崩溃报告收集能力、性能问题追踪能力等。

另外,Electron继承了Chromium的多进程架构Q,也是分一个主进程多个渲染进程的。

大致目录

Electron
├── build/ - 构建相关
├── buildflags/ - feature flag
├── chromium_src/ - chromium的一份拷贝(源码仅包含build文件)
├── default_app/ - 默认启动时的app程序
├── docs/ -文档
├── lib/ - 使用JS/TS编写的模块
|   ├── browser/ - 主进程初始化相关
|   |   ├── api/ - 主进程暴露的API,通过_linkedBinding调用C++模块
|   ├── common/ - 主进程和渲染进程共用代码
|   |   └── api/ - 主进程和渲染进程共同暴露的API
|   ├── renderer/ - 渲染进程初始化相关
|   |   ├── api/ - 渲染进程API
|   |   └── web-view/ - webview相关逻辑
├── patches/ - 关于依赖的一些patch,主要是chromium、node、v8的
├── shell/ - C++编写的模块
|   ├── app/ - 入口
|   ├── browser/ - 主进程相关
|   |   ├── ui/ - 系统UI组件的一些实现
|   |   ├── api/ - 主进程API实现
|   |   ├── net/ - 网络相关实现
|   |   ├── mac/ - Mac系统下的一些实现(OC实现)
|   ├── renderer/ - 渲染进程相关
|   |   └── api/ - 渲染进程API实现
|   └── common/ - 主进程渲染进程通用实现
|       └── api/ - 主进程渲染进程通用API实现
└── BUILD.gn - 构建入口

进程

主进程

每个 Electron 应用都有一个单一的主进程,作为应用程序的入口点。 主进程在 Node.js 环境中运行,这意味着它具有 require 模块和使用所有 Node.js API 的能力。

Electron 中,启动项目时运行的 main.js 脚本就是我们说的主进程。在主进程运行的脚本可以以创建 web 页面的形式展示 GUI

一个 Electron 应用有且只有一个主进程。并且创建窗口等所有系统事件都要在主进程中进行。

窗口管理

主进程的主要目的是使用 [BrowserWindow](https://link.juejin.cn/?target=https%3A%2F%2Fwww.electronjs.org%2Fzh%2Fdocs%2Flatest%2Fapi%2Fbrowser-window) 模块创建和管理应用程序窗口。

渲染器进程

每个 Electron 应用都会为每个打开的 BrowserWindow ( 与每个网页嵌入 ) 生成一个单独的渲染器进程。 洽如其名,渲染器负责 渲染 网页内容。 所以实际上,运行于渲染器进程中的代码是须遵照网页标准的。

这也意味着渲染器无权直接访问 require 或其他 Node.js API。 为了在渲染器中直接包含 NPM 模块,您必须使用与在 web 开发時相同的打包工具 (例如 webpackparcel)。

主进程和渲染进程之间的区别

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

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

主进程和渲染进程之间通信

Electron 的主进程是在后台运行,对应 main.js 文件。而渲染进程是前端看到的,对应 index.html 文件。这个两个进程之间的通信首选 ipc 方式,因为它会在完成时返回,而不会阻止同一进程中的其他操作。

  1. LocalStorage, window.postMessage

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

  1. IPC (Inner-Process Communication)

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

渲染进程发异步消息给主进程

// 渲染进程脚本
const { ipcRenderer } = require('electron')
// 发送异步消息
btns[0].addEventListener('click', () => {
    ipcRenderer.send('msg1', '这是一条来自于异步的消息')
})
// 监听消息
ipcRenderer.on('msg1Re', (ev, data) => {
    console.info(data)
})
// 主进程脚本
const { ipcMain } = require('electron')
ipcMain.on('msg1', (ev, data) => {
    console.info(data)
    // 发送消息给渲染进程
    ev.sender.send('msg1Re', '这是一条来自主进程的反馈消息')
})

渲染进程发同步消息给主进程

// 渲染进程脚本
const { ipcRenderer } = require('electron')
// 发送同步消息
btns[1].addEventListener('click', () => {
    const result = ipcRenderer.sendSync('msg2', '这是一条来自于同步的消息')
    console.info(result)
})
// 主进程脚本
const { ipcMain } = require('electron')
ipcMain.on('msg2', (ev, data) => {
    console.info(data)
    // 反馈消息
    ev.returnValue = '这是一条来自主进程的同步反馈消息'
})

主进程发消息给渲染进程

// 主线程脚本
BrowserWindow.getFocusedWindow().webContents.send(
    'mtp',
    '主进程发送消息给渲染进程'
)
// 渲染进程脚本
ipcRenderer.on('mtp', (ev, data) => {
    console.info(data)
})

渲染进程间通信

基于本地存储的渲染进程间通信

即采用 localStorage 机制

借助主进程,进行不同渲染进程间的通信
// 发起消息的渲染进程
ipcRenderer.send('mti', '这是条来自于 modal 的消息')
// 主进程
ipcMain.on('mti', (ev, data) => {
    // 通过 id 获取到对应的渲染进程,然后消息传递
    BrowserWindow.fromId(mainId).webContents.send('mti2', data)
})
// 接收消息的渲染进程 
ipcRenderer.on('mti2', (ev, data) => {
        console.info(data)
})
使用 sendTo

前提是要知道另一渲染窗口的 webContents 对应的 id 这边采用 global 存储窗口 ID

  const modalMain = new BrowserWindow({
          width: 200,
        height: 200,
        parent: BrowserWindow.fromId(mainId), // 这样关闭父窗口,则子窗口会一并关闭
        webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
        }
  })
  global.sharedObject =  {
    modalMainWebContentsId: modalMain.webContents.id
  }

然后在渲染进程里通过 getGlobal 来获取该 ID 值,并通过 sendTo 来发送消息

let sharedObject = getGlobal('sharedObject') 
let modalMainWebContentsId = sharedObject.modalMainWebContentsId
ipcRenderer.sendTo(modalMainWebContentsId, 'do-some-work', 1)

这样其他渲染进程就通过监听 do-some-work 来获取消息

ipcRenderer.on('do-some-work', (e, data) => {
    console.info(data)
  })

实践

参看教程:

无vite版本:https://juejin.cn/post/6983843979133468708

vite版本: https://blog.csdn.net/yanxinyun1990/article/details/130944508

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容