我眼中的 Electron

以前发过一个装逼的朋友圈:感谢 Electron,我如今有两个身份了:前端开发和 Mac 端开发。javascript

今年开始了一个全新的产品,但项目组并无 Mac 的开发人员,而后咱们前端就顺利的扛下了这面大旗,而且选择了火爆的 Electron(4w+ star)。html

年末了,看你们在写各类各样的年终总结,想着我和 Electron 也打情骂俏了好几个月,也该写点东西。不过 Electron 已经够火了,能 google 到不少相似如何使用 Electron 开发桌面端应用的文章,好比用Electron开发桌面应用,也能找到官方中文文档,虽然滞后了一点。因此我也就再也不赘述怎样从头写一个 Electron 应用了,在阅读下面的内容以前,最好能先跟随官方的 Quick Start 走一遍。前端

为何选择 Electron

Electron提供了一个Nodejs的运行时,专一于构建桌面应用,同时使用web页面来做为应用的GUI,你能够将其看做是一个由JavaScript控制的迷你版的Chromium浏览器。html5

够火自没必要说,Electron 的做者和 NW.js(原 node-webkit)是同一人,你们能够去知乎围观维护一个大型开源项目是怎样的体验?这个问题下做者的回答(评论区提出该知乎问题下还有一个回答对此存有异议:https://www.zhihu.com/questio...,你们自行分辨)。官方也有 Electron 和 NW.js 的对比,相信做者在新的项目中有了新的思考,因此选择 Electron 是个对的选择,而且咱们只须要开发 Mac 端(组内有 Windows 开发人员),没有对 XP 系统兼容的要求。java

也有考虑过像 MacGap 这样的解决方案,不过 MacGap 是基于 webkit 内核的,并不支持 IndexedDB,而咱们的项目依赖的第三方服务是依赖 IndexedDB 作为本地存储的。而且 Electron 更强大,好比新开 webview 加载第三方页面,而且能够预加载(preload) JavaScript来做为 jssdk 供 web 应用使用。node

因此 Electron,就是你了!git

主进程和渲染进程

我的感受,理解 Electron 最重要应该就是理解主进程(Main Process)和渲染进程(Render Process)了。理解了这二者,其余内容花费些时间查查API文档便可。github

Electron 中,入口是一个 js 文件(和 NW.js 不一样,入口是 html 文件),运行这个入口文件(一般会是 package.json 里的 main 脚本)的进程称做主进程,在主进程使用 BrowserWindow 模块能够建立并管理 web 页面,也就是应用的 GUI。web

const {BrowserWindow} = require('electron')
// 主进程建立web页面
let someWindow = new BrowserWindow(winOpts)
// 加载本地的文件
someWindow.loadURL('file://' + __dirname + '/index.html')

在主进程建立的一个个web页面也都运行着本身的进程,即渲染进程,渲染进程各自独立,各自管理本身的页面,能够想象是浏览器一个个的 tab。shell

进程间通讯

咱们知道,Web 页面由于安全限制,不能直接访问原生的GUI资源(好比dialog电源监控),Electron 中也是同样,渲染进程若是想要进行原生的GUI操做,就必须和主进程通信,请求相应的GUI操做。

Electron 提供了几种渲染进程和主进程通讯的方式:

一种是使用ipcMainipcRenderer模块,在渲染进程中使用ipcRender模块向主进程发送消息,主进程中ipcMain接收消息,进行操做,若是还须要反馈,则通知渲染进程,渲染进程根据接收的内容执行相应的操做:

// 渲染进程中
const {ipcRenderer} = require('electron')
ipcRender.send('somemsg', data);
ipcRender.on('replaymsg', (evt, otherData) => {
    console.log(otherData)
})

// 主进程中
const {ipcMain} = require('electron')
ipcMain.on('somemsg', (evt, data) => {
    console.log(data)
    evt.sender.send('replymsg', otherData);
});

// 同时Electron 也提供了同步的方式

不过切忌用 ipc 传递大量的数据,会有很大的性能问题,严重会让你整个应用卡住。

第二种是直接在渲染进程使用remote模块,remote 模块能够直接获取主进程中的模块。这种方式实际上是第一种方式的简化。

// 在渲染进程打开提示对话框
const {dialog} = require('electron').remote
dialog.showMessageBox({ opts });

第三种是主进程向渲染进程发送消息

this.webviewWindow.webContents.send('ping');

第四种是渲染进程之间的通讯

若是数据不须要实时性,只是渲染进程之间数据的共享,那么使用官方的建议便可:How to share data between web pages?。若是要求实时性,须要配合前几种种方式实现。

// 主进程
// 两个窗口互相获取对方的窗口 id, 并发送给渲染进程
win1.webContents.send('distributeIds',{
    win2Id : win2.id
});
win2.webContents.send('distributeIds',{
    win1Id : win1.id
});

// 渲染进程
// 经过 id 获得窗口
remote.BrowserWindow.fromId(win2Id).webContents.send('someMsg', 'someThing');

Nodejs集成

Electron 内集成了 Nodejs,大大的方便了开发。Nodejs 在主进程和渲染进程中均可以使用,上面说到,渲染进程由于安全限制,不能直接操做原生 GUI。虽然如此,由于集成了 Nodejs,渲染进程也有了操做系统底层 API 的能力,Nodejs 中经常使用的 Path、fs、Crypto 等模块在 Electron 能够直接使用,方便咱们处理连接、路径、文件MD5等,同时npm还有成千上万的模块供咱们选择。

尤为对于 Electron 不方便实现的功能,Nodejs 可能有奇效。咱们应用中用户须要下载文件消息的文件,须要支持同事下载多个,而且须要给出进度,Electron 并无提供一个好用的下载接口,因此咱们使用 Nodejs 的 http、fs 模块结合 Electron 的 dialog 模块实现了文件下载,而且实现了下载进度以及下载超时错误提示。

HTML5加强

不考虑兼容性应该是前端码农的梦想之一吧。Electron 使用 Chromium 来展现 web 页面,也就是咱们开发只须要兼容 Chromium 浏览器便可,也就是说好多属性能够肆无忌惮的用:播放语音直接使用 HTML5 audio、大量数据存储使用数据库 IndexedDB、难搞的布局直接使用 Flexbox、方便的检测在线离线等等。

同时 Electron 对一些 HTML5 的特性进行了加强:

  • 桌面通知,你能够直接使用 html5 的 notification,Electron会将其转化成为系统原生的桌面通知;

  • File 对象,在Web应用中咱们能获得的通常是相似 C:/fakePath/xxx.docx 的假路径,Electron在 File 对象上增长了一个path属性,能够用来获取选择的文件在文件系统中的真实路径。

  • a 标签的 download 属性,在 Web 应用中 a 标签增长 download 属性会强制浏览器下载,Electron 中会直接调起系统下载框下载,若是没有特殊需求推荐这种方式。

渲染进程调试和在浏览器中的调试彻底一致。前面提到每一个渲染进程彻底独立,当你建立了多个web页面,每一个页面均可以打开对应了调试工具,你能够和浏览器调试同样查看DOM、查看log、监听网络请求等等。
同时 Electron 集成了 Nodejs,因此你在控制台或者断点时也可以调试 Nodejs 的 API,甚至由于渲染进程可使用 remote 模块直接使用主进程的模块,你能够直接获取到这些数据以方便调试。

Electron 中的 Webview

此次的新产品有一个需求,须要在客户端内加载 Webview 应用,而且要提供 jssdk 供 Web 应用使用,以获取更多的本地能力。其实 Electron 自然的优点能够加载外部应用的。可是要考虑的问题仍是比较多的,好比要展现页面加载的进度、监听页面什么时候加载完成、页面 DOM 什么时候加载完成、服务端一些302重定向如何(好比一些跳转认证)处理、如何给 Web 应用提供 jssdk 等等。

Electron 提供了一系列的事件来监听页面的加载,细化到了页面开始加载、页面加载完成、页面加载失败、DOM Ready、框架加载 (did-frame-finish-load)、重定向(did-get-redirect-request)等等,经过监听这些事件能够对页面状态进行处理。

另外,如何给 Web 应用提供 jssdk 呢?咱们须要依赖 BrowserWindow 的一个配置项 - preload,preload 容许你指定一段脚本在页面加载以前载入,这段脚本你可使用 Electron 和 Nodejs 的 API,即便你在配置中不容许使用 Nodejs。

// preload 示例
var opts = {
    autoHideMenuBar: true,
    fullscreenable: false,
    webPreferences: {
        javascript: true,
        plugins: true,
        nodeIntegration: false, // 不集成 Nodejs
        webSecurity: false,
        preload: path.join(__dirname, 'preload/window_sdk.js') // 但预加载的 js 文件内仍可使用 Nodejs 的 API
    }
}

this.webviewWindow = new BrowserWindow(opts);

预加载 js 文件与其余 js 并没有二致,你只要根据你的业务,在 preload 的 js 中使用 remote 或者 ipc 通讯给你的 Web 应用提供够用接口就行了。

写在后面

Electron 并非很复杂,在写完很少的主进程代码后,其余的业务代码几乎和 Web 应用没什么区别,甚至能够将一个线上应用迅速的包装成为一个客户端应用,好比electronic-wechatworktile桌面端
不过坑不可避免(好比没法将 gif 写入剪切板等等),有时候也会感到很难像 Native 那样灵活,虽然如此,我仍是很欣慰能有这样的工具,让咱们前端能够作更多的事情。

最后多说一句:虽然Electron 的进程间通讯很方便,并且支持多窗口,但我仍是倾向于使用 Electron 构建单窗口应用,相似网易云音乐、Atom 等等,更简洁,思惟方式上更像咱们熟悉的 web 应用。

小广告

小前端FE博文的首发地址:http://blog.smallsfe.com
另外,欢迎咱们的微信公众号:小前端FE(smallsfe)

小前端FE(smallsfe)

相关文章
相关标签/搜索