BT

您是否属于早期采用者或者创新人士?InfoQ正在努力为您设计更多新功能。了解更多

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

| 作者 邱俊涛 发布于 2015年5月19日. 估计阅读时间: 3 分钟 | 智能化运维、Serverless、DevOps......2017年有哪些最新运维技术趋势?CNUTCon即将为你揭秘!

在这片文章中,我们将使用node-webkit来编写一个实际的例子,来说明如何使用HTML5+CSS3技术编写桌面应用。我们将通过给Moco工具编写一个简单的设计器来说明一些具体的技术,比如和本地文件的交互,实现简单的上下文菜单等。

Moco简介

Moco是一个用于简化服务器搭建的轻量级的测试工具,使用Moco可以很容易地创建一个“假的”服务器,这个服务器的行为就好像真实的服务器一样。Moco不但可以将集成测试变得更加简单,还可以搭建一个稳定的后端,从而使得纯前端的Web开发(或者前后端分离开发)变得平坦而容易。

Moco需要一个配置文件来指明request/response的对应关系:

[
     {
         "request": {
             "method": "post",
             "uri": "/action.do",
             "json_paths": {
                 "$.type": "events"
             }
         },
         "response": {
             "status": 200,
             "file": "spec/fixtures/events.json"
         }
     }
 ]

上例这个配置文件指定了:当发往/action.do的POST请求中,包含了JSONPath如$.type=events时,Moco会响应一个200的状态码,并且会返回文件spec/fixtures/events.json的内容作为响应。

每个配置文件中可以指定多条规则,当请求到达时,Moco会依次匹配,直到找到对应的规则,如果没有发现匹配,则返回400错误。

模板项目

我在github上创建了一个node-webkit的模板工程。你可以很从这个模板工程开始你的node-webkit之旅:

$ git clone git@github.com:abruzzi/node-webkit-boilerplate.git
 $ cd node-webkit-boilerplate
 $ npm install
 $ grunt

这个grunt命令会下载node-webkit,并构建应用程序:

构建之后,可以执行

$ ./launch.sh

启动应用程序,如果你看到了下面这个界面,说明基本的环境已经就绪,然后就可以进行下一步了。

引入bootstrap

bootstrap可以帮助我们快速地从零开始搭建一个专业的Web页面,bootstrap提供了丰富的特性,开箱即用的组件,我们这里只使用其中的布局器和表单组件。

使用bootstrap的一个模板HTML如下:

<!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="utf-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <title>node-webkit application</title>
 
     <!-- Bootstrap -->
     <link href="css/bootstrap.min.css" rel="stylesheet">
 </head>
 <body>
 <h1>Hello, node-webkit!</h1>
 <script src="js/jquery.min.js"></script>
 <script src="js/bootstrap.min.js"></script>
 <script src="js/app.js"></script>
 </body>
 </html>

有了bootstrap,接下来可以很容易地创建出专业的页面,比如在下面这个页面中,我们创建了几个列表:

<div class="row">
     <div class="media">
         <a class="pull-left" href="#">
             <img class="media-object" src="http://placehold.it/64x64">
         </a>
         <div class="media-body">
             <h4 class="media-heading">Listing object</h4>
             Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
             tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
             quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
             consequat.
         </div>
     </div>
     ...
     ...
 </div>

上面片段中的http://placehold.it/是一个提供图片占位符的服务,你只需要指定占位符的大小如64x64,这个服务就会返回给你一张对应尺寸的占位符。

有了这些元素之后就更像是一个桌面应用程序了。

Moco Services Designer

如前面所述,我们这篇文章中将要开发一个实际例子:moco服务器需要一个配置文件,但是moco本身提供了丰富的特性,因此要记住所有的选项非常困难,所以需要一个图形界面支持用户通过简单的操作就可以导出moco的配置文件:

点击界面中的加号会生成一个新的规则,每个规则分为请求和响应两部分,这里我们只提供最简单的选项:

  1. 用户可以选择使用那种HTTP动词(GET/POST/PUT/DELETE);
  2. 用户可以指定一个URI;
  3. 用户可以指定一个字符串作为返回;
  4. 用户可以定义多条规则;
  5. 访问本地文件。

首先在界面上有一个蓝色的加号:

<div class="row">
     <div class="col-lg-12 col-md-12">
         <a id="addRule" href="#" class="pull-right">
             <span class="glyphicon glyphicon-plus"></span>
         </a>
     </div>
 </div>
 
 <div id="rules">
 </div>

点击这个加号可以在页面上ID为rules元素中添加一个新的HTML片段:

$("#addRule").on("click", function() {
     $("#rules").append(fragement);
 });

这个片段来自另外一个文件:

<div class="row">
     <div class="panel panel-primary">
         <div class="panel-heading">Rule
             <a href="#" class="pull-right remove">
                 <span class="glyphicon glyphicon-minus"></span>
             </a>
         </div>
         <ul class="list-group">
             <li class="list-group-item">
                 <div class="input-group">
                     <select name="method">
                         <option value="get" selected="true">GET</option>
                         <option value="post">POST</option>
                         <option value="put">PUT</option>
                         <option value="delete">DELETE</option>
                     </select>
                 </div>
             </li>
             <li class="list-group-item">
                 Request
                 <div class="form-group">
                     <input type="text" class="form-control" name="uri" placeholder="/checkout">
                 </div>
             </li>
             <li class="list-group-item">
                 Response
                 <div class="form-group">
                     <textarea class="form-control" name="text" placeholder="content"></textarea>
                 </div>
             </li>
         </ul>
     </div>
 </div>

由于涉及本地文件的存取,我们需要引入node.js中的fs模块,首先在app目录中创建一个node_modules目录,并在该目录中创建一个file.js文件,内容如下:

 var fs = require('fs');
 
 function File() {
     function save(path, text) {
         fs.writeFile(path, text);
     }
 
     function read(path, callback) {
         fs.readFile(path, 'utf-8', callback);
     }
 
     this.save = save;
     this.read = read;
 }
 
 module.exports = new File;

我们导出了save和read两个方法可供使用,然后在外部的app.js中可以通过require的方式引入该文件:

var file = require('file.js');
 
 var fragement = null;
 file.read("rule.html", function(error, content) {
     fragement = content;
 });

要将页面上的内容保存到本地文件系统中,需要使用node-webkit提供的nwsaveas。

<input id="save" type="file" nwsaveas style="display:none" accept="text/*"/>

有了这个nwsaveas,我们可以为它注册一个change事件的回调,当用户选择了文件名(选择文件是一个典型的异步操作,保存文件的代码其实不知道用户在何时完成选择)之后,这个回调会执行。

一个简单的做法是定义一个隐藏的按钮,ID为save,一个可见的按钮ID为export,当export按钮被点击时,它触发save的点击,并且为change绑定回调函数。

$("#export").on("click", function() {
     chooseFile("#save");
 });
 
 function chooseFile(id) {
     var chooser = $(id);
     chooser.change(function(evt) {
         var config = generateMocoConf();
         var filename = $(this).val();
         file.save(filename, config);
     });
     chooser.trigger('click');
 }

这样,当用户完成选择之后,我们就有了文件名,这时候就可以将内容存入外部文件了。

当用户最终导出文件时,通过遍历所有的规则,然后生成一个JSON:

function generateMocoConf() {
     var rules = $("#rules .row");
     var config = [];
     $(rules).each(function(index, item) {
         var method = $('select[name="method"]', item).val();
         config.push({
             request: {
                 method: method,
                 uri: $('input[name="uri"]', item).val()
             },
             response: {
                 text: $('textarea[name="text"]', item).val()
             }
         });
     });
     return JSON.stringify(config, null, 4);
 }

右键菜单

到目前为止一切都还很顺利,你可以添加规则,保存规则到文件。但是你可能已经发现了,拷贝/粘贴等不工作,而且右键菜单也不见了。

我们需要自己构建一个右键菜单,比如最简单的拷贝/剪切/粘贴:

function Menu(cutLabel, copyLabel, pasteLabel) {
     var gui = require('nw.gui'),
         menu = new gui.Menu(),
         cut = new gui.MenuItem({
             label: cutLabel || "Cut",
             click: function() {
                 document.execCommand("cut");
             }
         }),
         copy = new gui.MenuItem({
             label: copyLabel || "Copy",
             click: function() {
                 document.execCommand("copy");
             }
         }),
         paste = new gui.MenuItem({
             label: pasteLabel || "Paste",
             click: function() {
                 document.execCommand("paste");
             }
         });
     menu.append(cut);
     menu.append(copy);
     menu.append(paste);
 
     return menu;
 }

nw.gui是node-webkit提供的一个包,它可以访问整个应用程序级别的对象,比如window,application等,详细的文档在这里[bd1]

我们可以通过这个包来创建一些MenuItem,并且在这些Item上绑定事件。最后,当鼠标右键时,显示这些菜单即可:

 var menu = new Menu();
 $(document).on("contextmenu", function(e) {
     e.preventDefault();
     menu.popup(e.originalEvent.x, e.originalEvent.y);
 });

完整的代码请参看github上的repo

作者简介

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


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

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

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

按照文中命令clone失败 by h yu

git clone git@github.com:abruzzi/node-webkit-boilerplate.git
这个命令在我的机器上运行失败,错误是这样的:
Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

后来我用github上显示的https地址clone成功了。

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

1 讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT