Electron基础与入门

815 阅读9分钟

一、Electron 介绍:

  • Electron 是什么?
    • Electron 是由 Github 开发的开源框架(它是由github开发,现在由OpenJS基金会维护的一个开源框架)

    • 它允许开发者使用 Web(html、css、js等)技术构建跨平台桌面应用;

    • Electron的核心组成是Chromium、Node.js以及内置的Native API。其中Chromium为Electron提供强大的UI能力,可以在不考虑兼容性的情况下,利用强大的web生态来开发界面;Nodejs让Electron有了底层的操作能力,比如像文件读写,集成c++等,还可以使用大量的npm包来帮助大家完成项目需求;内置的Native API解决了跨平台的问题,首先它提供了同一的原生界面,比如像窗口、托盘等,其次是系统能力,比如像我们的Notification等,最后是应用的基础能力,比如像软件更新,崩溃监控等。

image.png

  • 什么时候用Electron?
    • 特定领域:开发者工具、效率工具、实用工具、播放器、社交应用等;

    • 前端工程师储备;

    • 同时开发web + 桌面端

  • 学习Electron的好处?
    • 造工具,提高开发效率;

    • 技术广度

  • Electron最小组成:
graph TD
Electron --> main.js
Electron --> index.html
Electron --> package.json

main.js为主进程、index.html为渲染进程、package.json为包描述;下面我直接从官网搬一段过来:

// main.js
const { app, BrowserWindow } = require('electron')

const createWindow = () => {
    const win = new BrowserWindow({
        width: 800,
        height: 600
    })
    win.loadFile('index.html')
}
app.whenReady().then(() => {
    createWindow()
})

// index.html
<html>
  <head>
    <meta charset="UTF-8">
    <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
    <meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'">
    <title>Hello World!</title>
  </head>
  <body>
    <h1>Hello World!</h1>
    We are using Node.js <span id="node-version"></span>,
    Chromium <span id="chrome-version"></span>,
    and Electron <span id="electron-version"></span>.
  </body>
</html>

// package.json
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "Hello World!",
"main": "main.js",
"author": "Jane Doe",
"license": "MIT"
}

二、Electron架构原理及环境搭建

  • Electron架构

    • Chromium

      Electron它是基于Chromium做的,所以想要了解Electron的话要先了解Chromium架构。Chromium本质是Chrome的开源版,也是一个浏览器,浏览器也是一个桌面应用,它需要去创建窗口、右键菜单、管理浏览器Tab页还有扩展程序等等,而处理这些事情的进程我们称为主进程,也就是下图中的Browser;而对应的每个页面的进程我们称之为渲染进程(Render)。

      可以看到我们的浏览器会有一个主进程,多个渲染进程,进程之间是需要通信交互才能运转的,跨进程间的通信通过IPC来进行;我们主进程的RenderProcessHost和渲染进程的RenderProcess就是专门处理IPC事件的。

      渲染进程,我们最熟悉的页面就是在RenderView中基于Webkit排版展示出来的,ResourceDispatcher是用来处理我们的资源请求的,当我们的页面需要去请求资源的时候会通过ResourceDispatcher创建一个请求ID然后转发到我们的IPC在我们的主进程中处理,然后返回。

      本质上这张图带来的信息主要有三点:1、Chromium是多进程架构,包括Browser和多个Render;2、进程间是需要IPC通信的;3、我们Web关注到的只是很小的一部分。

      image.png

    • Electron

      由于Electron使用了Chromium来展示Web页面,所以Chromium的多进程架构也会被只用到Electron中,在Electron中也分为主进程和渲染进程。

      跟Chromium不一样的有两点,第一我们在各个进程里暴露了一些Native API,第二我们引入了Node.js。于是我们在Electron中可以使用Chromium和Node,比如我们可以通过Node去管理窗口,然后在页面中我们可以使用Node库。这其实很不容易的,因为在主线程中同一个时间下,只能运行一个事件循环。

      Node.js它的事件循环是基于libuv的,而Chromium是基于messagebump的,这就是Electron原理的重点(如何整合事件循环)。

      具体的思路有两种:

      • 将Chromium的messagebump用libuv实现一次,比如像NW就是这么做的,Electron其实也曾经尝试过,在渲染进程里面实现会比较简单,但是在主进程里面因为各个系统的GUI实现都不一样。
      • Node.js集成到Chromium,Chromium集成到Node.js(用libuv实现messagebump),这也是现在的做法。
  • 桌面端技术选型

    • 为什么要开发桌面端?
      • 更快捷的入口
      • 离线可用
      • 调用系统能力(通知、硬件…),连接打印机等
      • 安全需求
      • ......
    • 各大桌面端技术优缺点:
      • Native(C++/C#/Objective-C):
        • 高性能
        • 原生体验
        • 包体积小
        • 门槛高
        • 迭代速度慢
      • QT:
        • 基于C++
        • 跨平台(Mac、Windows、iOS、Android、Linux、嵌入式)
        • 高性能
        • 媲美原生的体验
        • 门槛高
        • 迭代速度一般
      • Flutter:
        • 跨端(iOS、Android、Mac、Windows、Linux、Web)
        • PC 端在发展中(Mac > Linux、Windows)
        • 基建少
      • NW.j:
        • 跨平台(Mac、Windows、Linux),v0.14.7 支持 XP(XP 市场份额约为15%)
        • 迭代快,Web 技术构建
        • 源码加密、支持 Chrome 扩展
        • 不错的社区
        • 包体积大
        • 性能一般
      • Electron:
        • 跨平台(Mac、Windows、Linux、不支持 XP)
        • Web 技术构建
        • 活跃的社区
        • 大型应用案例
        • 包体积大
        • 性能一般 具体对比如下图:
      image.png
  • 环境搭建

    • 编辑器,自己喜欢就好

    • node,建议版本不要太高,可以通过nvm来管理node版本,具体安装参考: juejin.cn/post/695364…

    • Electron安装:

      • npm install electron --save-dev
      • npm install --arch=ia32 --platform=win32 electron

      我们可以指定arch=ia32来安装32位的electron,在window平台打包都应该要基于32位来打,这样打出来的包在32位和64位都可以用

二、与web开发的不同

  • 主进程与渲染进程:

    • 主进程:
      • Electron 运行 package.json 的 main 脚本的进程被称为主进程
      • 每个应用只有一个主进程
      • 管理原生 GUI,典型的窗口(BrowserWindow、Tray、Dock、Menu)
      • 创建渲染进程
      • 控制应用生命周期(app)
    • 渲染进程:
      • 展示 Web 页面的进程称为渲染进程

      • 通过 Node.js、Electron 提供的 API 可以跟系统底层打交道

      • 一个 Electron 应用可以有多个渲染进程

      image.png
  • 进程间的通信

    • 进程间通信的目的
      • 通知事件(比如我们在页面中想去创建一个原生菜单,但只有主进程才能够去创建原生菜单,所以只能通过IPC进程通信,去让主进程创建我们的菜单)
      • 数据传输(比如我想在某个页面里获得现在的内存情况,这样的话我就需要将这些数据通过IPC来进行传输)
      • 共享数据(比如我们的用户信息在各个进程之间都会用到,就需要通过IPC通信来完成数据的共享)
    • IPC模块通信
      • Electron 提供了 IPC 通信模块,主进程的 ipcMain 和 渲染进程的 ipcRenderer
      • ipcMain、ipcRenderer 都是 EventEmitter 对象
    • 如何通信
      • 从渲染进程到主进程:
        • Callback 写法:ipcRenderer.send、ipcMain.on

          // 渲染进程
          ipcRenderer.send('my-click', '我点击了hello');
          
          // 主进程
          ipcMain.on('my-click', (e, val) => {
              console.log(val);
          })
          
          
        • Promise 写法(Electron 7.0 之后,处理请求 + 响应模式):ipcRenderer.invoke、ipcMain.handle(我们在渲染进程中发送事件,并且可以得到主进程中的处理结果,类似于发送请求)

          // 渲染进程
          const { ipcRenderer } = require('electron');
          
          const hNode = document.getElementById('btn');
          hNode.onclick = async function() {
              let res = await ipcRenderer.invoke('my-click', '我是渲染进程的数据');
              console.log(res) // 我是渲染进程的数据:我又从主进程过来了
          }
          
          // 主进程
          ipcMain.handle('my-click', async (e, val) => {
              let res = await new Promise((resolve, reject) => {
                  resolve(val + ':' + '我又从主进程过来了') 
              })
              return res;
          })
          
          
      • 从主进程到渲染进程
        • 主进程通知渲染进程:ipcRenderer.on、webContents.send(这里可能有的小伙伴会感到疑惑,为什么不像渲染进程到主进程一样通过send、on呢?这是因为,主进程只有一个而渲染进程有多个,我们到底send给谁呢?因此我们需要找到具体的窗体内容,也就是webContents)

          // 主进程
          // 等渲染进程加载完毕
          win.webContents.send('my-main', '我是主进程过来的');
          
          // 渲染进程
          ipcRenderer.on('my-main', (e, val) => {
              console.log(val); // 我是主进程过来的
          })
          
      • 页面间(渲染进程与渲染进程间)通信
        • 通知事件:通过主进程转发(Electron 5之前,渲染进程发送给主进程,再由主进程转发给另一个渲染进程)、ipcRenderer.sendTo (Electron 5之后)
        • 数据共享:Web 技术(localStorage、sessionStorage、indexedDB)、使用 remote(将我们数据挂载到全局,容易影响性能,electron10以后remote弃用,需要自行npm下载)
      • 注意事项:
        • 少用remote模块,甚至是不用它,很多人会因为它写起来特别简单,会常用它,但是每次remote会触发底层的同步IPC事件,特别影响性能,甚至如果remote处理的不好,会导致进程卡死;
        • 不用sync模式,一旦写的不好,会导致整个应用卡;
        • 在请求+响应的通信模式下,需要自定义超时限制,当我们的应用响应超时的时候,需要直接response一个异常的超时事件让业务处理,然后去做对应的交互。
  • Native能力及原生GUI

    • 使用 Electron API 创建原生 GUI

      • BrowserWindow 应用窗口
      • Tray 托盘
      • app 设置dock.badge
      • Menu 菜单
      • dialog 原生弹框
      • TouchBar 苹果触控蓝
      • ...
    • 使用 Electron API 获得底层能力

      • clipboard 剪切板
      • screen
      • globalShortcut 全局快捷键
      • desktopCapture 捕获桌面
      • shell 打开本地文件、URL
      • powerMonitor
      • ...
    • 使用 Node.js 获得底层能力

      • Electron 同时在主进程和渲染进程中对 Node.js 暴露了所有的接口
        • fs 进行文件读写
        • crypto 进行加解密
      • 通过 npm 安装即可引入社区上所有的 Node.js 库
    • 使用 Node.js 调用原生模块

      • node.js add-on
      • node-ffi(调用c++等动态库,比如像打印这种模块下)
    • 调用 OS 能力

      • WinRT(windows runtime)
      • Applescript(苹果系统)
      • Shell(系统命令)
    • Electron 的能力

      image.png

  • 释放前端想象力

    • 无兼容问题
      • 不用担心在 Safari、IE 上的表现差异了
      • 大胆使用 Chrome 浏览器已经支持的 API
      • babel 中设置 targets 为 Electron 对应的 Chrome 版本
    • 最新浏览器 Feature
    • ES 6/7/8/9/10高级语法
      • Async await / Promise
      • String/Array/Object 等高级用法
      • BigInt
    • 无跨域问题
      • 使用 Node.js 发送请求
      • 使用 Electron net 发送请求
    • More
      • 操作本地文件
      • 更好用的本地 DB
      • 多线程、多进程并行

文章内容非原创,只为学习记录,如有侵权,联系删除。