中国领先的IT技术网站
|
|
创建专栏

Electron 实战桌面计算器应用

Electron 是一个搭建跨平台桌面应用的框架,仅仅使用 JavaScript、HTML 以及 CSS,即可快速而容易地搭建一个原生应用。这对于想要涉及其他领域的开发者来说是一个非常大的福利。

作者:林鑫|2017-09-05 16:43

Tech Neo技术沙龙 | 11月25号,九州云/ZStack与您一起探讨云时代网络边界管理实践


Electron 是一个搭建跨平台桌面应用的框架,仅仅使用 JavaScript、HTML 以及 CSS,即可快速而容易地搭建一个原生应用。这对于想要涉及其他领域的开发者来说是一个非常大的福利。

项目介绍

仓库地址:lin-xin/calculator

我这里通过 Electron 实现了仿 iPhone 的计算器,通过菜单可以切换横屏和竖屏,横屏有更多的运算。而对于 JavaScript 进行浮点数计算来说,精度丢失是个很大问题,所以我这里使用了第三方库 math.js 来解决这个精度的问题。

尽可能的实现了跟 iPhone 一样的运算:

  • 1 + 2 × 3 = 7
  • 3 += 6 (再按 = 等于 9)
  • 0.1 + 0.2 = 0.3 (浮点数精度处理)

不过我下面并不是要讲计算器,而是用到的 Electron 的知识点。

生命周期

在主进程中通过 app 模块控制整个应用的生命周期。

当 Electron 完成初始化时触发 ready 事件:

  1. app.on('ready', () => { 
  2.     // 创建窗口、加载页面等操作 
  3. }) 

当所有的窗口都被关闭时会触发 window-all-closed 事件:

  1. app.on('window-all-closed', () => { 
  2.     if(process.platform !== 'darwin'){ 
  3.         app.quit();     // 退出应用 
  4.     } 
  5. }) 

在开发中发现,没有监听该事件,打包后的应用关闭后,进程还保留着,会占用系统的内存。

窗口

本来我们的 html 只显示在浏览器中,而 electron 提供了一个 BrowserWindow 模块用于创建和控制浏览器窗口,我们的页面就是显示在这样的窗口中。

创建窗口

通过关键字 new 实例化返回 win 对象,该对象有丰富的方法对窗口进行控制。

  1. win = new BrowserWindow({ 
  2.     width: 390,         // 窗口宽度 
  3.     height: 670,        // 窗口高度 
  4.     fullscreen: false,  // 不允许全屏 
  5.     resizable: false    // 不允许改变窗口size,不然布局就乱了啊 
  6. }); 

加载页面

窗口创建完是一片空白的,可以通过 win.loadURL() 来加载要显示的页面。

  1. const path = require('path'); 
  2. const url = require('url'); 
  3. win.loadURL(url.format({    // 加载本地的文件 
  4.     pathname: path.join(__dirname, 'index.html'), 
  5.     protocol: 'file'
  6.     slashes: true 
  7. })) 

也可以直接加载远程链接 win.loadURL(‘http://blog.gdfengshuo.com‘);

菜单

桌面应用菜单栏是最常见的功能。Electron 提供了 Menu 模块来创建原生的应用菜单和 context 菜单,

  1. const template = [                              // 创建菜单模板 
  2.     { 
  3.         label: '查看'
  4.         submenu: [ 
  5.             {label: '竖屏', type: 'radio', checked: true},      // type 属性让菜单为 radio 可选 
  6.             {label: '横屏', type: 'radio', checked: false}, 
  7.             {label: '重载',role:'reload'}, 
  8.             {label: '退出',role:'quit'}, 
  9.         ] 
  10.     } 
  11. const menu = Menu.buildFromTemplate(template);  // 通过模板返回菜单的数组 
  12. Menu.setApplicationMenu(menu);                  // 将该数组设置为菜单 

在子菜单中,通过点击竖屏或横屏来进行一些操作,那就可以给 submenu 监听 click 事件。

  1. const template = [ 
  2.     { 
  3.         label: '查看'
  4.         submenu: [ 
  5.             { 
  6.                 label: '横屏' 
  7.                 click: () => {              // 监听横屏的点击事件 
  8.                     win.setSize(670,460);   // 设置窗口的宽高 
  9.                 } 
  10.             } 
  11.         ] 
  12.     } 

主进程和渲染进程通信

虽然点击横屏的时候,可以设置窗口的宽高,但是要如何去触发页面里的方法,这里就需要主进程跟渲染进程之间进行通信。

主进程,可以理解为 main.js 用来写 electron api 的就是主进程,渲染进程就是渲染出来的页面。

ipcMain

在主进程中可以使用 ipcMain 模块,它控制着由渲染进程(web page)发送过来的异步或同步消息。

  1. const {ipcMain} = require('electron'
  2. ipcMain.on('send-message', (event, arg) => { 
  3.     event.sender.send('reply-message''hello world'
  4. }) 

ipcMain 监听 send-message 事件,当消息到达时可以调用 event.sender.send 来回复异步消息,向渲染进程发送 reply-message 事件,也可以带着参数发送过去。

ipcRenderer

在渲染进程可以调用 ipcRenderer 模块向主进程发送同步或异步消息,也可以收到主进程的相应。

  1. const {ipcRenderer} = require('electron'
  2. ipcRenderer.on('reply-message', (event, arg) => { 
  3.     console.log(arg);       // hello world 
  4. }) 
  5. ipcRenderer.send('anything''hello everyone'); 

ipcRenderer 可以监听到来自主进程的 reply-message 事件并拿到参数进行操作,也可以使用 send() 方法向主进程发送消息。

webContents

webContents 是一个事件发出者,它负责渲染并控制网页,也是 BrowserWindow 对象的属性。在 ipcMain 中的 event.sender,返回发送消息的 webContents 对象,所以包含着 send() 方法用于发送消息。

  1. const win = BrowserWindow.fromId(1);        // fromId() 方法找到ID为1的窗口 
  2. win.webContents.on('todo', () => { 
  3.     win.webContents.send('done''well done!'
  4. }) 

remote

remote 模块提供了一种在渲染进程(网页)和主进程之间进行进程间通讯(IPC)的简便途径。在 Electron 中,有许多模块只存在主进程中,想要调用这些模块的方法需要通过 ipc 模块向主进程发送消息,让主进程调用这些方法。而使用 remote 模块,则可以在渲染进程中调用这些只存在于主进程对象的方法了。

  1. const {remote} = require('electron'
  2. const BrowserWindow = remote.BrowserWindow      // 访问主进程中的BrowserWindow模块 
  3. let win = new BrowserWindow();                  // 其他的跟主进程的操作都一样 

remote 模块除了可以访问主进程的内置模块,自身还有一些方法。

  1. remote.require(module)          // 返回在主进程中执行 require(module) 所返回的对象 
  2. remote.getCurrentWindow()       // 返回该网页所属的 BrowserWindow 对象 
  3. remote.getCurrentWebContents()  // 返回该网页的 WebContents 对象 
  4. remote.getGlobal(name)          // 返回在主进程中名为 name 的全局变量(即 global[name]) 
  5. remote.process                  // 返回主进程中的 process 对象,等同于 remote.getGlobal('process') 但是有缓存 

shell 模块

使用系统默认应用管理文件和 URL,而且在主进程和渲染进程中都可以用到该模块。在菜单中,我想点击子菜单打开一个网站,那么就可以用到 shell.openExternal() 方法,则会在默认浏览器中打开 URL

  1. const {shell} = require('electron'); 
  2. shell.openExternal('https://github.com/lin-xin/calculator'); 

打包应用

其实将程序打包成桌面应用才是比较麻烦的事。我这里尝试了 electron-packager 和 electron-builder。

electron-packager

electron-packager 可以将项目打包成各平台可直接运行的程序,而不是安装包。

先使用 npm 安装: npm install electron-packager -S

运行打包命令:

  1. electron-packager ./ 计算器 --platform=win32 --overwrite --icon=./icon.ico 

打包会把项目文件包括 node_modules 也一起打包进去,当然可以通过 –ignore=node_modules 来忽略文件,但是如果项目中有用到第三方库,忽略的话则找不到文件报错了。

正确的做法就是严格区分 dependencies 和 devDependencies,打包的时候只会把 dependencies 的库打包,而使用 cnpm 安装的会有一大堆 .0.xx@xxx 的文件,也会被打包,所以最好不要用 cnpm

electron-builder

electron-builder 是基于 electron-packager 打包出来的程序再做安装处理,将项目打包成安装文件。

安装:npm install electron-builder -S

打包:electron-builder –win

打包过程中,第一次下载 electron 可能会出现连接超时,可以使用 yarn 试试。还有 winCodeSign 和 nsis-resources 也可能会失败,可以参考 electron-builder/issues 解决。

总结

Electron 用起来还是相对容易的,可以创建个简单的桌面应用,只是打包的过程比较容易遇到问题,网上好像也有一键打包的工具,没尝试过。以上也都是基于 windows 7 的实践,毕竟没有 Mac 搞不了。

【本文为51CTO专栏作者“林鑫”的原创稿件,转载请通过微信公众号联系作者获取授权】

戳这里,看该作者更多好文

【编辑推荐】

  1. 【iOS开发】简易加法计算器的实现
  2. Linux桌面系统的优势
  3. 外媒速递:GNOME仍然是Linux桌面系统中的No.1
  4. Tomcat 是怎样处理 SpringBoot应用的?
  5. LimitLatch 在 Tomcat 中的应用
【责任编辑:武晓燕 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

热门职位+更多

× CTO训练营(深圳站)