使用node-webkit构建桌面应用程序(一)

2015 年 5 月 14 日

Web 前端的现状

目前 Web 前端的现状较之 5~6 年前,简直不能同日而语:从所使用的技术、工具、框架到开发一个产品所需要付出的工作量,从前端开发从业人员的数量到 Web 应用的数量,从企业对于 Web 前端的重要程度的认识到 Web 实际上为企业带来的回报,一切都有了翻天覆地的变化。

借助 HTML5+CSS3 的普及,加上一些开箱即用的 CSS 框架(如 bootstrap,foundation 等)支持,人们已经可以非常容易地从零开始搭建一个 Web 应用的前端。一个在 UI 方面非常业余的程序员也可以很快做出一个像模像样的用户界面。而另一方面,基于操作系统原生 API,要想设计并实现一个桌面应用,需要的付出则远远超过同水平的 Web 界面。

Webkit 浏览器内核

Webkit 作为最受欢迎的浏览器内核,自然有非常多的 port。比如 GTK+ 对它的 port – WebkitGTK,以及构建在 WebkitGTK 之上的 Python 的 bind(即 Python 对 WebkitGTK 的本地动态库的一个包装,可以使用 Python 的语法来编写代码,但是底层仍然使用 WebkitGTK 自身的动态库)。使用 WebkitGTK 的 Python 版本,开发人员可以用 HTML+CSS 来开发应用,然后写一点 Python 脚本,最后将其运行在桌面上。

这里有个早期的例子来教你如何写一个所见即所得的编辑器。桌面应用开发中,用户界面的复杂性一直是一个难题,而这种方式可以降低很多用户界面开发的复杂性,将界面开发交给更加灵活、更加容易编写和调试的方式——HTML+CSS。

这种模式下基本的开发流程是编写一个 HTML 页面(作为程序入口),然后在这个页面上引入额外的 CSS(界面风格)和 JavaScript(动作),然后将这些资源交给工业级浏览器内核 Webkit 来渲染 。 这个过程和在浏览器中访问该文件并无二致,但是有两个额外的好处。

  • 页面运行在一个“桌面应用程序”中。 没有地址栏,状态栏,菜单栏等,看起来更像是一个桌面应用。
  • 用户界面开发的复杂性被“外包”给一个更简单的环境。 这就是传说中的混合(hybrid)开发模式,比如现在移动开发中的 cordova 就是采用这种模式,使得本来被视为天堑的原生的用户界面开发变为坦途。

node-webkit 简介

node-webkit 是一个基于 chromium 和 node.js 的应用程序开发工具。它不但支持使用传统的 HTML5+CSS3+JS 方式来开发应用程序,还支持无缝地与 Node.js 集成,也就是说,所有的 Node 支持的与操作系统交互的功能,如网络连接,文件系统,操作系统资源访问等,以及 Node 之上的第三方库都可以在 node-webkit 中进行使用。

更好的是,node-webkit 是一个跨平台的工具,你可以使用它构建出运行在 Mac OS,Linux 以及 Windows 下的应用程序。应用程序通过 Node.js 来进行与系统相关的访问,而用 HTML5+CSS3 进行用户界面部分的设计。

node-webkit 未必是未来桌面应用的唯一方式,但是却是一个非常好的选择,特别对于已经熟知 Web 前端开发技术栈的众多开发者来说,无需学习一门新的语言,一切都在很大程度上得到了简化。

第一个 node-webkit 应用程序

开发 node-webkit 应用程序非常简单。在这里下载系统对应的版本。并确保对应的二进制文件(nwnw.exe) 在系统的 PATH 之中。

创建一个新的目录,然后在该目录中创建一个 package.json 文件和一个 index.html 文件:

复制代码
$ mkdir -p hello-node-webkit
$ cd hello-node-webkit
$ touch package.json index.html

package.json 文件的内容如下:

复制代码
{
"name": "hello-node-webkit",
"version": "0.1.0",
"main": "index.html"
}

index.html 文件的内容如下:

复制代码
<html>
<head>
<title>Hello node-webkit</title>
</head>
<body>
<div>
<h1>Hello node-webkit</h1>
</div>
</body>
</html>

然后将这两个文件打在一个 zip 格式压缩包中:

复制代码
$ zip -r hello-node-webkit.zip *

然后将这个文件重命名为 hello-node-webkit.nw,最后使用 node-webkit 来启动这个应用程序。

复制代码
$  ~/Tools/node-webkit.app/Contents/MacOS/node-webkit hello-node-webkit.nw

添加外部 JS/CSS

接下来我们为这个页面添加一些外部的引用:CSS/JavaScript 文件。首先创建两个目录 style 和 script,然后分别创建文件如下:

复制代码
├── index.html
├── package.json
├── script
│ ├── app.js
│ └── jquery.min.js
└── style
└── style.css

其中,style.css 定义了 h1 的简单样式:

复制代码
h1 {
font-size: 20px;
color: #999999;
}

而 app.js 则注册了一个简单的事件处理器:

复制代码
$(function() {
$("h1").on("click", function() {
alert("Heading 1 is clicked");
});
});

此时的 index.html 修改如下:

复制代码
<html>
<head>
<title>Hello node-webkit</title>
<link rel="stylesheet" type="text/css" href="style/style.css">
</head>
<body>
<div>
<h1>Hello node-webkit</h1>
</div>
<script type="text/javascript" src="script/jquery.min.js"></script>
<script type="text/javascript" src="script/app.js"></script>
</body>
</html>

还是按照上一小节的命令完成打包,改名,启动之后。点击 h1 元素时,会弹出对话框如下:

在这个例子中,我们使用了外部的 css 文件来添加样式,还引入了 jQuery 作为访问 DOM 元素的工具,最后还使用了一段调用 jQuery 的 JavaScript 代码。

构建脚本

你可能已经注意到了,使用 node-webkit 开发非常方便。但是这一系列的动作(修改 HTML+CSS,压缩打包,改名,启动)等有一部分重复工作,我们可以将其自动化。

好在已经有了一个很好用的 grunt 的插件:grunt-node-webkit-builder,这个插件可以帮助我们自动执行压缩打包这些动作。

复制代码
$ npm install grunt-node-webkit-builder

然后定义一个 Gruntfile.js,这个文件中指定源文件(所有的 HTML,JavaScript 代码,CSS 文件)所在目录,目标文件所在目录,需要构建的应用程序指定的操作系统平台等:

复制代码
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
nodewebkit: {
options: {
platforms: ['osx'],
buildDir: 'builds',
},
src: ['app/**/*']
}
});
grunt.loadNpmTasks('grunt-node-webkit-builder');
grunt.registerTask('default', ['nodewebkit']);
};

这样,我们修改之后,就可以直接执行:

复制代码
$ grunt

进行打包了。比如在 Mac 下,构建出来的应用位于 builds/ /osx 目录下,要启动该应用只需要在命令行输入下列命令:

复制代码
$ open builds/hello-node-webkit/osx/hello-node-webkit.app

或者在 Finder 中双击打开即可。

可以看到上例中的应用程序还有浓重的浏览器痕迹,比如地址栏,刷新按钮,甚至还有一个 DevTools 的按钮。

我们可以通过修改 package.json 来指定:

复制代码
{
"name": "hello-node-webkit",
"version": "0.1.0",
"main": "index.html",
"window": {
"toolbar": false,
"width": 800,
"height": 600
}
}

这样的界面就更像是一个桌面应用了:

到目前为止,这个小的应用程序并没有什么有趣的特性,用户界面也毫无美感,但是有了这些基本知识和工具之后,我们就可以开始更进一步的开发了。除了使用既有的 CSS 框架来完成用户界面的美化,我们还会使用 node.js 访问系统资源来构建真实的应用程序。

一个实际的小例子

gist 是一个非常好用的代码片段管理工具,Github 的用户可以将自己的代码片段保存为一个 gist,每个 gist 中可以包含若干个文件,每个 gist 可以通过一个唯一的 URL 指定,比如: https://gist.github.com/abruzzi/44550f2b4cb812b19701

我们这里将开发一个具体的小例子:通过 Node.js 请求到一个 gist,并将其中的所有文件名都列出来,最终的结果看起来是这样的:

首先我们需要安装一个 npm 的包:gisty,这个包提供的 API 可以方便地获取 gist。安装命令如下:

复制代码
$ cd app/
$ npm install gisty

注意此处的 app 目录为我们的应用目录,其中包含了 HTML 文件,CSS,及 JavaScript 等,在这个目录中执行 npm install 会创建一个 node_modules 目录,gisty 会安装在这个目录中。

然后我们修改 scripts/app.js,内容如下:

复制代码
var Gisty = require('gisty');
$(function() {
var gist = new Gisty({
username: 'abruzzi'
});
gist.fetch('44550f2b4cb812b19701', function(error, gist) {
if (error) {
throw new Error(error);
}
buildGistItem(gist);
});
function buildGistItem(gist) {
var gists = $("#gists");
for (filename in gist.files) {
var gistItem = $("<li></li>");
gistItem.text(filename);
gistItem.appendTo(gists);
}
}
});

注意此处的 require 语句,这正是使用 node-webkit 的强大之处:可以在“前端”代码中使用“后端”代码。gisty 需要指定用户名来创建一个 Gisty 对象, 然后在这个对象上调用 fetch 方法,并将 gist 的 ID 传入来获取 gist 对象。gist 对象上有 files 属性,其中保存该 gist 对应的文件名和文件内容。

使用 gisty 的 API 获取到 gist 列表之后,将其插入在页面元素上。HTML 页面上声明了一个 ID 为 gists 的 ul,然后在 app.js 中动态的创建 li 来添加到该 ul 上。

复制代码
<html>
<head>
<title>Hello node-webkit</title>
<link rel="stylesheet" type="text/css" href="style/style.css">
</head>
<body>
<div>
<h1>Gist #44550f2b4cb812b19701</h1>
</div>
<div>
<ul id="gists"></ul>
</div>
<script type="text/javascript" src="script/jquery.min.js"></script>
<script type="text/javascript" src="script/app.js"></script>
</body>
</html>

作者简介

邱俊涛,ThoughtWorks 咨询师,喜欢技术,崇尚轻量级的开发 / 工作方式,痛恨冗长的会议和各种繁琐的流程。他还是《JavaScript 核心概念及实践》一书的作者,个人博客是 http://icodeit.org ,博客上经常会有各种技术的分享。


感谢张凯峰对本文的策划,丁晓昀对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们,并与我们的编辑和其他读者朋友交流。

2015 年 5 月 14 日 09:3617656

评论

发布
暂无评论

LeetCode题解:49. 字母异位词分组,数组计数+哈希表,JavaScript,详细注释

Lee Chen

LeetCode 前端进阶训练营

Architecture Phase1 Week4:Summarize

phylony-lu

极客大学架构师训练营

架构师训练营第三周心得

CmHuang

架构师训练营 - 命题作业 - 第三周

徐时良

极客大学架构师训练营

架构师训练营 Week4 - 课后作业

缓存 自动化 异步 集群 冗余

架构师训练营第四周学习总结

邓昀垚

极客大学架构师训练营

源码分析怎么做?

tison

源码分析

第二节课后作业

happy

区块链--凌驾于政治之上的存在

CECBC区块链专委会

区块链 信息技术

spring-boot-route(六)整合JApiDocs生成接口文档

Java旅途

Java Spring Boot

架构师训练营 Week4 系统架构 - 学习总结 架构演进

Chrome浏览器架构

曲迪

chrome 前端 浏览器 专栏

架构师训练营第 1 期 - 第 3 周 - 学习总结

wgl

极客大学架构师训练营

Web Storage API的介绍和使用

程序那些事

web tech web storage web storage api storage api

Architecture Phase1 Week4:HomeWork

phylony-lu

极客大学架构师训练营

区块链带来第四次技术革命 融入生产大幅提高企业收入

CECBC区块链专委会

区块链 数字资产 技术革命

小伙伴想学Jenkins自动构建发布项目,我:安排上了!!

冰河

项目管理 jenkins 灰度发布 自动构建 及时发布

【知识分享】区块链常用术语

CECBC区块链专委会

区块链 货币 网络节点

金秋十月重磅技术文——网络编程大揭秘

Java架构师迁哥

编程 程序员

作者谈《阿里巴巴Java开发手册(规约)》背后的故事

Java架构师迁哥

架构师训练营 - 学习笔记 - 第三周

徐时良

极客大学架构师训练营

Serverless Frist 的渐进式应用开发框架 Malagu

木香丘

开源 Serverless 云原生 Malagu Framework

架构师训练营第 1 期 - 第 3 周 - 作业

wgl

极客大学架构师训练营

Linux搭建C++开发调试环境

MySQL从删库到跑路

c++ Linux gdb 编译

云服务器网站打开速度过慢,如何进行自检

德胜网络-阳

spring-boot-route(五)整合Swagger生成接口文档

Java旅途

Java springboot swagger

第三周-代码重构

Galaxy数据平台

阿里P8大牛爆肝的《Java核心技术总结》+《面试题总结》简直赞爆了

Java架构之路

Java 程序员 面试 编程语言 进阶

理想的程序员

极客思享

如何开一场高效的会议?

boshi

高效工作 开会

一个草根的日常杂碎(10月5日)

刘新吾

随笔杂谈 生活记录 社会百态

使用node-webkit构建桌面应用程序(一)-InfoQ