原文:Creating Your First Desktop App With HTML, JS and Electron

作者:Danny Markov

近年来 web 应用变得越来越强大,但是桌面应用仍然有充分利用硬件的优势。

今天,我们可以通过我们熟悉的 HTML、JS 和 Node.js 来创建桌面应用,打包成一个可执行文件,并且发布在 Windows, OS X 和 Linux 上。

有两个受欢迎的开源项目,能够帮助我们实现这个目的。一个是几个月前我们讨论到的 NW.js,另一个是今天我们将要使用的 Electron(点击这里查看它们的不同之处)。我们将把旧项目的 NW.js 重构成 Electron,所以你可以轻松地比较他们的不同。

开始使用 Electron

用 Electron 构建的应用仅仅是一个网站而已,这个网站在嵌入了 Chromium 的浏览器中运行。除了常规的 HTML5 接口,这些应用能够使用所有的 Node.js 模块,以及 Electron 特有的模块,这个模块能够让应用拥有访问操作系统的能力。

这篇文章中,我们将会构建一个简单的应用,通过 RSS 订阅,来获取 Tutorialzine 上最新的文章,并通过好看的轮播的形式展现出来。这个项目用到的所有文件,你可以点击文章顶部的下载按钮获取(译者注:下载链接)。

下载后,解压内容到一个文件夹。从文件结构来看,你永远猜不到这是一个桌面应用,而不仅仅是一个简单的网站。

接下来,我们将仔细分析这些项目文件,了解它们是如何工作的。但首先,我们来运行这个应用。

运行应用

由于 Electron 应用只是一个 Node.js 应用,你需要安装 npm。你可以点击这里学习怎样安装,这很容易。

解压项目后,在解压目录下打开 cmd 或者命令行,执行以下命令:

  1. npm install

这条命令会创建一个 node_modules 文件夹,里面包含了项目需要所有的 Node.js 依赖。接下来,在同个终端上执行下面的命令:

  1. npm start

这个应用会打开一个新的窗口。注意看,窗口有一个顶部菜单以及其他东西。

你可能已经留意到,这个应用的启动方式不怎么友好。但这仅仅是开发时启动应用的方式。应用打包发布后,就会像普通的程序那样只是一个安装文件,双击图标就可以启动。

它是如何工作的

这一段内容,我们将讨论 Electron 应用必要的文件。我们将从 package.json 文件讲起,这个文件包含关于这个项目的各种信息,比如版本号、npm 依赖以及其他重要的配置。

  1. package.json
  2. {
  3. "name": "electron-app",
  4. "version": "1.0.0",
  5. "description": "",
  6. "main": "main.js",
  7. "dependencies": {
  8. "pretty-bytes": "^2.0.1"
  9. },
  10. "devDependencies": {
  11. "electron-prebuilt": "^0.35.2"
  12. },
  13. "scripts": {
  14. "start": "electron main.js"
  15. },
  16. "author": "",
  17. "license": "ISC"
  18. }

如果你之前使用过 Node.js,你已经知道了这个文件是做什么的。在这里最值得讲述的是 scripts 这个属性,它定义了 npm start 这条命令,允许我们使用命令来运行应用。当我们执行这条命令的时候,electron 就会执行 main.js。这个 JS 文件的脚本打开了应用的窗口,定义了一些配置和事件处理。

main.js

  1. var app = require('app'); // Module to control application life.
  2. var BrowserWindow = require('browser-window'); // Module to create native browser window.
  3. // Keep a global reference of the window object, if you don't, the window will
  4. // be closed automatically when the JavaScript object is garbage collected.
  5. var mainWindow = null;
  6. // 当所有窗口关闭时退出应用
  7. app.on('window-all-closed', function() {
  8. // On OS X it is common for applications and their menu bar
  9. // to stay active until the user quits explicitly with Cmd + Q
  10. if (process.platform != 'darwin') {
  11. app.quit();
  12. }
  13. });
  14. // This method will be called when Electron has finished
  15. // initialization and is ready to create browser windows.
  16. app.on('ready', function() {
  17. // 创建浏览器窗口
  18. mainWindow = new BrowserWindow({width: 900, height: 600});
  19. // 加载这个应用的 index.html
  20. mainWindow.loadURL('file://' + __dirname + '/index.html');
  21. // 当窗口关闭时执行
  22. mainWindow.on('closed', function() {
  23. // Dereference the window object, usually you would store windows
  24. // in an array if your app supports multi windows, this is the time
  25. // when you should delete the corresponding element.
  26. mainWindow = null;
  27. });
  28. });

我们来看一下,在 ready 方法做了哪些事情。首先我们定义了一个浏览器窗口,并且设置了它的初始化大小。然后,在窗口里面加载了 index.html 文件,这一步就像在浏览器中打开了一个 HTML 文件。

正如你所看到的,这个 HTML 文件本身并没有什么特别——它包含了一个轮播组件的容器标签和一个用来展示 CPU 和 RAM 的 p 标签。

index.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1">
  6. <title>Tutorialzine Electron Experiment</title>
  7. <link rel="stylesheet" href="./css/jquery.flipster.min.css">
  8. <link rel="stylesheet" href="./css/styles.css">
  9. </head>
  10. <body>
  11. <div class="flipster">
  12. <ul>
  13. </ul>
  14. </div>
  15. <p class="stats"></p>
  16. <!-->In Electron, this is the correct way to include jQuery<-->
  17. <script>window.$ = window.jQuery = require('./js/jquery.min.js');</script>
  18. <script src="./js/jquery.flipster.min.js"></script>
  19. <script src="./js/script.js"></script>
  20. </body>
  21. </html>

这个 HTML 文件也引用了几个必要的样式文件,JS 库和脚本。注意在这里 jQuery 是通过一种奇怪的方式引入进来的。更多相关信息可以查看这个issue

最后,是这个应用的实际脚本文件。脚本里面,我们调用 Tutorialzine 的 RSS 订阅接口,获取了最新的文章并且展示出来。如果我们在浏览器环境这么做,是获取不到数据的,因为跨域的原因,我们无法获取到数据。但在 Electron 里就没有跨域的限制,我们可以简单地通过 AJAX 请求来获取我们需要的信息。

  1. $(function(){
  2. // Display some statistics about this computer, using node's os module.
  3. var os = require('os');
  4. var prettyBytes = require('pretty-bytes');
  5. $('.stats').append('Number of cpu cores: <span>' + os.cpus().length + '</span>');
  6. $('.stats').append('Free memory: <span>' + prettyBytes(os.freemem())+ '</span>');
  7. // Electron's UI library. We will need it for later.
  8. var shell = require('shell');
  9. // 获取 Tutorialzine 上最新的文章
  10. var ul = $('.flipster ul');
  11. // The same-origin security policy doesn't apply to electron, so we can
  12. // send ajax request to other sites. Let's fetch Tutorialzine's rss feed:
  13. $.get('http://feeds.feedburner.com/Tutorialzine', function(response){
  14. var rss = $(response);
  15. // Find all articles in the RSS feed:
  16. rss.find('item').each(function(){
  17. var item = $(this);
  18. var content = item.find('encoded').html().split('</a></div>')[0]+'</a></div>';
  19. var urlRegex = /(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:/~\+#]*[\w\-\@?^=%&amp;/~\+#])?/g;
  20. // Fetch the first image of the article.
  21. var imageSource = content.match(urlRegex)[1];
  22. // Create a li item for every article, and append it to the unordered list.
  23. var li = $('<li><img /><a target="_blank"></a></li>');
  24. li.find('a')
  25. .attr('href', item.find('link').text())
  26. .text(item.find("title").text());
  27. li.find('img').attr('src', imageSource);
  28. li.appendTo(ul);
  29. });
  30. // Initialize the flipster plugin.
  31. $('.flipster').flipster({
  32. style: 'carousel'
  33. });
  34. // 文章被点击时,在系统默认浏览器打开连接
  35. // 不这样做的话,会在 electron 窗口中打开连接,这不是我们想要的效果
  36. $('.flipster').on('click', 'a', function (e) {
  37. e.preventDefault();
  38. // 在默认浏览器中打开链接
  39. shell.openExternal(e.target.href);
  40. });
  41. });
  42. });

上面的代码中,比较酷的是,我们在一个文件同时使用了:

  • JavaScript 库 - 使用 jQuery 和 jQuery Flipster 来生成轮播效果。
  • Electron 原生模块 - Shell 这个模块提供了桌面相关的任务接口,在示例中,我们通过这个接口调用默认浏览器中打开了一个链接。
  • Node.js 模块 - 用来访问操作系统信息的 OS 模块,用来格式化的 Pretty Bytes 模块。通过这些模块,我们完成了这个应用!

打包和发布

还有一件重要的事需要我们去做,就是准备好我们的应用给最终用户使用。你需要把项目打包成一个可执行文件,可以在用户的机器上双击运行。由于 Electron 应用可以在多个不同的操作系统上运行,我们需要分别打包成 Windows、OS X 和 Linux 各个版本。像 Electron Packager 这个 npm 模块就是一个不错的工具。

需要考虑的是,打包项目时会把所有资源,所有的 Node.js 依赖,加上一个缩小版的 WebKit 浏览器,打包成一个可执行的文件。这些东西加起来的结果就是,打包后的应用体积大约为 50mb。这个体积已经算是比较大了,对于一些简单的应用,比如这个示例项目来说,有些不太理想。但对于那些比较大而又复杂的项目来说,就无关紧要了。

总结

从这个示例项目可以看出,Electron 与 NW.js 的主要差异在于,NW.js 是直接打开一个 HTML 页面,而 Electron 是通过执行一个脚本文件,在代码中创建一个窗口。Electron 这种创建方式,能够让我们更大程度地控制应用,比如我们可以轻松地构建一个多窗口应用,并在窗口之间进行通信。

整体上,Electron 是一个令人激动的、通过 web 技术来构建桌面应用的方式。接下来,你可以继续阅读:

我(译者)的总结

  • 翻译这篇文章,主要是因为这篇文章已经有中文译文了,翻译完成后,我可以对比其他译文,总结不足之处。对比了一下,差距还是很大的。
  • 翻译到一半才发现,运行效果和文中的截图不一致。调试发现,是因为 Tutorialzine 的接口不能用了。这导致翻译出来的文章实用性大大降低,这一点,以后需要注意。
  • 文章没有讲到如何打包 Electron 应用,有点可惜。有时间我会补充,或者另写一篇。
  • 一年前就接触了 Electron,当时 Electron 还有很多不足,现在 Electron 改进了一些,比较看好 Electron 的发展。
  • 这是我的第一篇翻译,万事开头难,中间更难,结尾最难,还需继续努力。

(译)通过 HTML、JS 和 Electron 创建你的第一个桌面应用的更多相关文章

  1. Electron - 创建跨平台的桌面客户的应用程序

    Electron 框架的前身是 Atom Shell,可以让你写使用 JavaScript,HTML 和 CSS 构建跨平台的桌面应用程序.它是基于io.js 和 Chromium 开源项目,并用于在 ...

  2. (译)iPhone: 用公开API创建带小数点的数字键盘 (OS 3.0, OS 4.0)

    (译)iPhone: 用公开API创建带小数点的数字键盘 (OS 3.0, OS 4.0) 更新:ios4.1现在已经将这个做到SDK了.你可以设置键盘类型为UIKeyboardTypeDecimal ...

  3. 【译】在ASP.NET中创建PDF-iTextSharp起步

    原文 [译]在ASP.NET中创建PDF-iTextSharp起步 .Net framework 中自身并不包含可以和pdf打交道的方法.所以,当你需要你的ASP.Net Web应用程序中包含创建或与 ...

  4. electron-vue:Vue.js 开发 Electron 桌面应用

    相信很多同学都知道 Electron 可以帮助开发人员使用前端技术开发桌面客户端应用,今天介绍的 electron-vue 框架是一套基于 Vue.js 开发 Electron 桌面应用的脚手架,该项 ...

  5. js函数的创建

    1.js 函数的创建有几种方式: 1.1  直接声明 1.2 创建匿名函数,然后赋值 1.3 声明函数,然后赋值给变量 1.4 使用1.3 得到的变量再赋值给变量 1.5 使用函数对象创建函数 < ...

  6. js如何动态创建表格(两种方法)

    js如何动态创建表格(两种方法) 一.总结 一句话总结: 1.方法一:写好创建表格的html代码,将之赋值给div的innerHTML. 2.方法二.直接用创建好的table元素的方法insertRo ...

  7. react快速上手一(使用js语法,创建虚拟DOM元素)

    1.装包,引包 首先需要安装两个包 react ,react-dom cnpm i react react-dom 介绍下这两个包: react:专门用来创建React组件,组件生命周期等这些东西. ...

  8. 用Node开发桌面应用:NW.js和Electron

    NW.js和Electron对比:[http://tangiblejs.com/posts/nw-js-electron-compared] NW.js:[https://nwjs.io/] Elec ...

  9. 本页面用来演示如何通过JS SDK,创建完整的QQ登录流程,并调用openapi接口

    QQ登录将用户信息存储在cookie中,命名为__qc__k ,请不要占用 __qc__k : 1) :: 在页面顶部引入JS SDK库: 将“js?”后面的appid参数(示例代码中的:100229 ...

随机推荐

  1. java使用字节流和字符流实现文件复制

    大家在Java开发中都会遇到文件复制的文件,众所周知,需要通过文件输入输出流实现. 那究竟该怎么做那,话不多说,直接上代码: 一,使用字节流复制文件 public class FileByteCopy ...

  2. 【转】Java虚拟机的JVM垃圾回收机制

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp43       1.JVM内存空间     JVM堆(Heap)= 新生代 ...

  3. Java 强引用 软引用 弱引用 虚引用详解

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt393 众所周知,java中是JVM负责内存的分配和回收,这是它的优点(使用方 ...

  4. C++中模板Template的使用

    1. 在c++Template中很多地方都用到了typename与class这两个关键字,而且好像可以替换,是不是这两个关键字完全一样呢?class用于定义类,在模板引入c++后,最初定义模板的方法为 ...

  5. JMeter打开脚本失败 如何解决?

    最近有碰到JMeter打开之前的脚本,报错了,见下图: 后来发现这是因为之前保存脚本的 jmeter 和这次打开脚本的 jmeter 版本不一致(图一)或者版本一致而插件没有保持同步(图二)的原因: ...

  6. 【深入Java虚拟机】之七:Javac编译与JIT编译

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/18009455 编译过程 不论是物理机还是虚拟机,大部分的程序代码从开始编译到最终转化成物理 ...

  7. Java 多线程(三) 线程的生命周期及优先级

    线程的生命周期 线程的生命周期:一个线程从创建到消亡的过程. 如下图,表示线程生命周期中的各个状态: 线程的生命周期可以分为四个状态: 1.创建状态: 当用new操作符创建一个新的线程对象时,该线程处 ...

  8. 201521123001《Java程序设计》第8周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 2. 书面作业 本次作业题集集合 List中指定元素的删除(题目4-1) 1.1 实验总结 答: 在老师的详细 ...

  9. Java多态总结

    面向对象的三大特性:封装.继承.多态.从一定角度来看,封装和继承几乎都是为多态而准备的.这是我们最后一个概念,也是最重要的知识点. 1.定义: 多态:指允许不同类的对象对同一消息做出响应.即同一消息可 ...

  10. Sublime使用Ctrl+`作为快捷键弹出Console没有反映的解决办法

    很多Sublime新人都遇到了这个问题,到网上搜,信息很片面,而且不少都是旧版本的.于是有了这篇文章.       默认Sublime使用Ctrl+`作为快捷键弹出Console,但不同的系统抑或安装 ...