在這片文章中,我們將使用node-webkit來編寫一個實際的例子,來說明如何使用HTML5+CSS3技術編寫桌面應用。我們將通過給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可以幫助我們快速地從零開始搭建一個專業的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服務器需要一個配置文件,但是moco本身提供了豐富的特性,因此要記住所有的選項非常困難,所以需要一個圖形界面支持用戶通過簡單的操作就可以導出moco的配置文件:
點擊界面中的加號會生成一個新的規則,每個規則分為請求和響應兩部分,這里我們只提供最簡單的選項:
首先在界面上有一個藍色的加號:
<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,博客上經常會有各種技術的分享。