electron 构建跨平台桌面应用

时间:2022-04-25
本文章向大家介绍electron 构建跨平台桌面应用,主要内容包括简介、Hello World、案例分析、功能模块、remote、webview、其他、打包构建、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

昨日(2016.09.13)本文发表后,获得了一定的阅读和转发量,但经部分网友反馈和仔细审核后发现,在与 NW.js 对比的环节,言辞欠妥,且数据的真实性有待考究,特此将争议部分删除,同时借此诚挚地向 NW.js 的作者以及各位读者反馈表示感谢,期待更多深入的交流和分享,修订后的版本如下:

Stack Overflow 联合创始人 Jeff Wood 曾说过,任何一个能用 JavaScript 编写的应用系统,最终都必将使用 JavaScript 实现。

简介

Electron 是一款可以通过 Web前端技术 构建跨平台桌面应用的框架。其原名为 Atom Shell, 是 Github 社区原本为 Atom 编辑器设计的一个跨平台应用外壳,它将 Chromium 和 Node.js 的事件循环整合在一起,并提供了一些与原生系统交互的 API。

简单地说,通过 Electron,我们可以使用自己所熟悉的前端技术轻松构建出一款能运行在Windows, Linux 和 Mac 上的桌面级应用程序。

现阶段已有许多优秀的桌面应用都是基于 Electron 开发,其中如 Atom 编辑器,VS Code 和 Postman 等等都是我们所熟知的,下面列出这当中的部分应用,是不是看到了许多熟悉的图标呢?

Hello World

案例运行

使用下面三步即可构建最简单的 Hello World 桌面程序。

1. 克隆官方的例子
$ git clone https://github.com/electron/electron-quick-start

2. 进入项目目录
$ cd electron-quick-start

3. 安装项目依赖后执行该程序
$ npm install && npm start

运行效果如下:

案例分析

察看目录结构如下:

.
├── .gitignore    
├── index.html
├── LICENSE.md
├── main.js
├── package.json
├── README.md
└── renderer.js

主要分为三个部分:

  • package.json: 定义了项目的依赖以及程序的主入口文件 main.js。
  • main.js: 负责创建应用窗口,并赋予其与当前操作系统的原生GUI交互的功能。
  • index.html: 定义了页面的渲染内容,即 “Hello World” 字符串。

Electron 程序启动时,会产生两条进程,分别是主进程和渲染进程,main.js 脚本执行的环境就是主进程,负责管理和维护着渲染进程的生命周期,拥有绝大部分 node模块 的调用能力;而在 main.js 中创建的每一个窗体则对应着一个渲染进程,它们之间相互独立,且都具备部分 Node模块 的功能。

主进程与渲染进程的关系如下图所示,它们之间通过 IPC 模块进行消息交互,关于 IPC 模块的使用,下面会提到。

功能模块

这个部分将介绍 Electron 里面常用到的几个功能模块。

IPC

上面提到,Electron 中包含了主进程和渲染进程,事实上主进程就是一个后台进程,掌控着渲染进程的创建与销毁动作,且官方提供的绝大部分模块也只能在该进程中调用。

主进程与渲染进程之间的通信通过 IPC(进程间通信)模块完成,IPC模块可划分为 ipcMain 和 ipcRenderer 两个部分,其中 ipcMain 对应 主进程中的 IPC模块,而 ipcRenderer 则是在渲染进程中使用,下面直接看个例子:

main.js:

// 引入 ipcMain 模块
const ipcMain = require('electron').ipcMain;
// 监听 ‘blabla’ 通道,收到消息后输出,并向 'blibli' 通道发送消息
ipcMain.on('blabla', function(event, arg) {
    console.log(arg);
    event.sender.send('blibli', 'hello client!');
})

index.html:

// 引入 ipcRenderer 模块
const ipcRenderer = require('electron').ipcRenderer;  
// 向 'blabla' 通道发送消息
ipcRenderer.send('blabla', 'hello server!');
// 监听 ‘blibli’ 通道, 收到消息后输出
ipcRenderer.on('blibli', function(event,arg) {
  console.log(arg);
});

输出效果正如你所期望那样:

main.js:

hello server!

index.html:

hello client!

remote

上面提到了大部分模块只能在主进程中调用,为了突破这种限制,Electron 官方还提供了 remote 模块以简化进程间的通讯。

通过 remote 模块,渲染进程可以方便地引用主进程中的模块和全局变量等。

main.js:

global.person = {
  name: 'boxizen',
  sex : 'male',
  age : 24
}

index.html:

// 引用 remote 模块
const remote = require('electron').remote;
// 输出 main.js 中定义的 person 全局对象的 age 属性值
console.log(remote.getGlobal('person').age);

输出效果(index.html):

24

webview

webview 是个比较有趣的标签,可以将线上的页面嵌入进 Electron app 中,与 iframe 不同的是,webview 和应用运行的是不同的进程,不拥有渲染进程的权限。

下面将演示如何将微信网页版嵌入进 Electron 应用里,只需要简单的两步:

index.html:

<webview autosize="on" src="https://wx.qq.com/" style="display:inline-flex; width:1000px; height:764px"></webview>

main.js:

mainWindow = new BrowserWindow({width: 1000, height: 764, resizable: false})

效果图:

这样一个PC版的微信就大功告成了,实际上就是利用 webview 标签加载微信网页版的在线地址,再在main.js中调整窗体大小以适配网页版的微信,是不是很简单呢。

webview 对象中包含 insertCSS()executeJavaScript() 两个方法,表示可以插入样式代码和执行 js 脚本,这样我们就可以对加载页面中的样式及交互逻辑进行修改。默认的 webview 没有 node 功能,而如果设置了 nodeintegration 属性,它将整合node,拥有可以使用系统底层的资源。

此外 webview 中的 preload 属性允许在页面的脚本执行前预加载一个指定的脚本,下面我们利用该属性和 executeJavaScript() 方法实现 electron 版微信的未读消息角标展示。

index.html:

// 这里使用 preload 属性预加载了 badge.js 脚本
<webview id="foo" autosize="on" preload="badge.js" src="https://wx.qq.com/" style="display:inline-flex; width:1000px; height:764px"></webview>

var webview = document.getElementById("foo");      
  webview.addEventListener('dom-ready', function () {             
  webview.executeJavaScript('badge.get()');
});

badge.js:

// 引入 IPC 模块
const ipcRenderer = require('electron').ipcRenderer;

badge = {
  get: function () {  
      // 监听微信左侧面板节点变化
    $(".panel").bind('DOMSubtreeModified', function() {          
      var count = 0;       
      // 累加所有未读消息   
      $(".icon.web_wechat_reddot_middle").each(function () {
        count += parseInt(this.textContent);
      });
      // 通过 IPC 发送给主进程
      if (count > 0) {
        ipcRenderer.send('badge-changed', count.toString());
      } else {
        ipcRenderer.send('badge-changed', '');
      }
    })
  }
}

main.js:

const electron = require('electron');
const ipcMain = electron.ipcMain;
const app = electron.app

exports.init = function() {
    // 监听角标通道消息
    ipcMain.on('badge-changed', function (event, num) {
      app.dock.setBadge(num);
    });
}

效果图如下,可以发现 Electron 的 dock 角标显示的未读消息数(11)跟微信中面板中未读消息数量一致:

其他

当然 Electron 中还有许多实用的模块,如作为桌面应用必不可少的 MenuTray 模块、拥有调用当前操作系统功能的 Shell 模块、NW.js 中不具备的自动更新功能 - autoUpdater 模块、自动提交奔溃报告的 crashReporter 模块和全局快捷键模块 global-shortcut 等等,此处不做过多介绍。

打包构建

Electron 打包的方式有很多种,常见的有 electron-builder、electron-packager 和 asar几种,在这里我使用的是 electron-packager 作为应用的打包工具。

首先还是得先安装 electron-packager:

npm install electron-packager --save-dev 

然后在 package.json 中编写构建命令,下面生成了分别在 Windows 和 Mac 下的两条构建命令:

"scripts": {
    "start": "electron .",
    "build-win": "electron-packager . doubanFM --platform=win32 --arch=x64 --version=1.2.6 --icon=./fm.ico --overwrite",
    "build-mac": "electron-packager . doubanFM --platform=darwin --arch=x64 --version=1.2.6 --icon=./fm.icns --overwrite"
  },

执行构建命令, done!

npm run build-mac

最后贴一张最近利用 Electron 构建的桌面版豆瓣FM的截图:

参考资料: