什么是JavaScript桌面應用在我心中,桌面應用一直占據著一個特殊的地位。隨著瀏覽器,移動設備變得越來越強大,被移動和web應用取代的桌面應用呈穩(wěn)定下滑趨勢。但編寫桌面應用還是有很多優(yōu)勢的--它們會一直存在于你的開始目錄或者Dock欄中,可以被alt(cmd)+tab來回切換,并且大部分比web應用與底層系統(tǒng)聯(lián)系的更緊密(快捷鍵,通知推送等)。 本文中,我會引導你用JavaScript創(chuàng)建一個簡單桌面應用,接觸相關的概念。
用JavaScript編寫桌面應用的核心思想是編寫一套資料庫,把它分別打包來兼容各個操作系統(tǒng)。不需要創(chuàng)建原生桌面應用的知識,維護起來更簡單。現在,用JavaScript開發(fā)桌面應用主要是使用Electron或者NW.js。盡管兩種工具提供的功能相似,我更喜歡Electron,因為它有一些我認為很重要的優(yōu)勢。到頭來,你用哪一個都沒有問題。 基本假定我假定你已經有了文本編輯器(或者IDE),并且安裝了Node.js和npm。同樣假定你已經掌握HTML/CSS/JavaScript的知識(如果會Node.js和CommonJS模塊更好,不過并不是必需的),這樣可以將重點放在學習Electron,而不需要擔心創(chuàng)建用戶界面(其實就是普通的web頁面)。如果你不符合前面幾點,你也許會感到有些迷惑,我推薦你看下我的前一篇文章來學習基礎。 Electron 概述簡單來說,Electron為用純JavaScript創(chuàng)建桌面應用提供了運行時。原理是,Electron調用你在package.json中定義的main文件并執(zhí)行它。main文件(通常被命名為main.js)會創(chuàng)建一個內含渲染完的web頁面的應用窗口,并添加與你操作系統(tǒng)的原生GUI(圖形界面)交互的功能。 詳細地說,當用Electron啟動一個應用,會創(chuàng)建一個主進程。這個主進程負責與你系統(tǒng)原生的GUI進行交互并為你的應用創(chuàng)建GUI(在你的應用窗口)。
僅啟動主進程并不能給你的應用用戶創(chuàng)建應用窗口。窗口是通過main文件里的主進程調用叫BrowserWindow的模塊創(chuàng)建的。每個瀏覽器窗口會運行自己的渲染進程。渲染進程會在窗口中渲染出web頁面(引用了CSS,JavaScript,圖片等的HTML文件)。web頁面是Chromium渲染的,因為各系統(tǒng)下標準是統(tǒng)一的的,所以兼容性很好。 舉例來說,如果你有一個計算器應用,主進程會初始化一個窗口來呈現實際的web頁面(計算器)。 雖說只有主進程才和系統(tǒng)原生GUI交互,還是有技術可以把部分任務轉到渲染進程中運行。 主進程通過一套可直接調用的Electron模塊與原生GUI交互,桌面應用可以使用所有的Node模塊,如用node-notifier模塊來推送系統(tǒng)通知,request模塊來發(fā)起HTTP請求等。 Hello, world!練習用資料庫現在,讓我們做好準備,用傳統(tǒng)的「Hello,World」來開始。 本指南的同步練習資料庫是sound-machine-tutorial。首先把資料庫clone下來: git clone https://github.com/bojzi/sound-machine-electron-guide.git 進入sound-machine-tutorial文件夾,用下面的代碼在git的tag之間切換: git checkout <tag-name> 我會提示你該用哪個tag: 請切換至: 當你clone完代碼,切換到你想要的tag,運行: npm install 這樣你安裝好全部Node模塊了。 如果你不能轉換到另一個tag,最簡單的辦法是重置你的資料庫狀態(tài)再切換: git add -A 開始 請切換到00-blank-repository這個tag: 在項目文件夾中新建package.json文件,寫入下面的內容: 這個簡單的package.json文件:
現在該安裝Electron了,最簡單的方式是用npm為你的操作系統(tǒng)安裝預構建的二進制文件。并在package.json文件中將它設置為開發(fā)依賴(用--save-dev命令后綴自動設置)。在CLI中運行命令: npm install --save-dev electron-prebuilt 預構建的二進制文件是為所在的操作系統(tǒng)量身訂造的,可以運行「npm start」。我們將它作為開發(fā)依賴安裝是因為只在開發(fā)過程中用到它。 就這樣,Electron開發(fā)所需的一切都準備好了。 跟世界打個招呼新建app文件夾,在其中新建有下面代碼的index.html文件:
在項目的根目錄下新建一個main.js文件。Electron的主進程將用它來啟動并創(chuàng)建「Hello, world」桌面應用。main.js中的代碼: 'use strict';
var app = require('app');
var BrowserWindow = require('browser-window');
var mainWindow = null;
app.on('ready', function() {
mainWindow = new BrowserWindow({
height: 600,
width: 800
});
mainWindow.loadUrl('file://' + __dirname + '/app/index.html');
});
沒什么嚇人的,不是嗎? 「app」模塊會控制應用的生命周期(例如, 對應用的ready狀態(tài)做出反應)。 「BrowserWindow」模塊為你創(chuàng)建窗口。 「mainWindow」對象是你應用的主窗口,被聲明成null,否則當JavaScript垃圾回收掉這個對象時,窗口會被關閉。 當「app」捕獲ready事件,「BrowserWindow」創(chuàng)建一個800*600大小的窗口。 瀏覽器窗口的渲染進程會渲染index.html文件。 在CLI中鍵入下面命令啟動「Hello, World!」: npm start 現在為你的第一個Electron程序歡呼吧。
開發(fā)真正的應用超棒的發(fā)聲器首先,什么是發(fā)聲器? 發(fā)聲器當你點擊不同按鈕時會播放不同聲音的小設備,大部分是卡通或特效聲。是在辦公室用來放松心情的,很有趣的工具,隨著開發(fā)的進行,會碰到的很多新的概念,所以這也是一個很好的開發(fā)桌面應用的實例(也是一個非常棒的發(fā)聲器)。
我們將完成的功能和探索的概念包括:
實現發(fā)聲器的基礎功能應用的結構 你已經實現了一個運行正常的「Hello World!」應用,現在是時候實現一個發(fā)聲器應用了。 典型的發(fā)聲器功能包括幾排按鈕,點擊播放聲音,這些聲音大部分是卡通式的或者特效式的(如大笑,鼓掌,玻璃碎裂聲等)。 那就是我們要完成的第一個功能--能對點擊能做出響應的發(fā)聲器。
應用結構是比較簡單的。 在應用的根目錄,保存著package.json文件,main.js文件和其他文件。 app文件夾保存HTML文件,其中在css,js,wav和img文件夾中保存相應類型的文件。 為了簡便,web頁面所需的全部文件都已經保存在資料庫的初始狀態(tài)中?,F在切換到01-start-project這個tag。如果你之前跟著指南操做,創(chuàng)建了「Hello World!」應用,你需要先重置資料庫再切換: If you followed along with the "Hello, world!" example: 切換到01-start-project這個tag: 為了簡便,發(fā)聲器將只有兩種聲音,但擴展到全部16種聲音也非常簡單,只需要其他聲音和圖標文件,修改index.html就可以。 完成主進程 用main.js定義發(fā)聲器的外觀。用下面的代碼代替原內容: 'use strict';
var app = require('app');
var BrowserWindow = require('browser-window');
var mainWindow = null;
app.on('ready', function() {
mainWindow = new BrowserWindow({
frame: false,
height: 700,
resizable: false,
width: 368
});
mainWindow.loadUrl('file://' + __dirname + '/app/index.html');
});
我們用傳給「app」模塊的尺寸參數,自定義了新建窗口的大小,設定它是固定尺寸并且無邊欄。它會浮在你的桌面上,就像真的發(fā)聲機一樣。 現在的問題是 -- 如何移動一個沒有邊欄的窗口(沒有標題欄),如何關閉它? 我很快就會講解自定義窗口(應用)關閉(并介紹一種主進程和渲染進程通信的方法),但拖動部分很簡單,在index.css(app/css文件夾下)文件中: html,
body {
...
-webkit-app-region: drag;
...
}
-webkit-app-region:drag;使整個html變成一個可拖動的對象?,F在有一個問題,你不能點擊可拖動對象里的按鈕。答案就是-webkit-app-region: no-drag;能定義不可拖動(但是可以點擊)的對象,參考index.css的中的代碼: .button-sound {
...
-webkit-app-region: no-drag;
}
在窗口中顯示發(fā)聲器 main.js文件現在可以新建一個窗口來顯示發(fā)聲器。如果用npm start啟動應用,你可以看到發(fā)聲器非常逼真。現在點擊沒有反應,這并不奇怪,我們只有一個靜態(tài)的web頁面。 添加下面的代碼到index.js(app/js文件夾)文件中會添加交互效果: 'use strict';
var soundButtons = document.querySelectorAll('.button-sound');
for (var i = 0; i < soundButtons.length; i++) {
var soundButton = soundButtons[i];
var soundName = soundButton.attributes['data-sound'].value;
prepareButton(soundButton, soundName);
}
function prepareButton(buttonEl, soundName) {
buttonEl.querySelector('span').style.backgroundImage = 'url("img/icons/' + soundName + '.png")';
var audio = new Audio(__dirname + '/wav/' + soundName + '.wav');
buttonEl.addEventListener('click', function () {
audio.currentTime = 0;
audio.play();
});
}
代碼很簡單,我們:
CLI中輸入下面命令來測試應用: npm start
用遠程事件從瀏覽器窗口關閉應用請切換到02-basic-sound-machine這個tag: 簡要重述--應用窗口(更準確的說是渲染進程)應該不能與GUI(用來關閉窗口)通信的,官方的Electron快速入門指南寫到: 在web頁面,不允許調用原生GUI相關的API,因為在web頁面管理原生GUI資源是很危險的,會很容易泄露資源。如果你想在web頁面施行GUI操作,web頁面的渲染進程必須要與主進程通信,請求主進程來完成這些操作。 Electron提供ipc(進程間通信)模塊來實現這類通信。ipc模塊可實現從通道訂閱消息,發(fā)送消息給通道的訂閱者,通道區(qū)分消息的接收者,用字符來標識(例如,通道1,通道2)。消息也可以包含數據。當接收到消息,訂閱者可以做出反應,甚至回復消息。消息最大的好處就是隔離 -- 主進程不必知道哪個渲染進程發(fā)出消息。
這正是我們在做的 -- 主進程(main.js)訂閱「close-main-window」通道的消息,關閉按鈕被點擊時,渲染進程(index.js)通過通道發(fā)出消息。 在main.js里添加下面的代碼,從通道訂閱消息: var ipc = require('ipc');
ipc.on('close-main-window', function () {
app.quit();
});
引入ipc模塊后,通過通道訂閱消息就變得很簡單,on()方法設置訂閱的通道名,定義回調函數。 渲染進程要通過通道發(fā)送消息,將下面代碼加入index.js: var ipc = require('ipc');
var closeEl = document.querySelector('.close');
closeEl.addEventListener('click', function () {
ipc.send('close-main-window');
});
同樣,我們引入ipc模塊,給關閉按鈕的元素綁定一個click事件。當點擊關閉按鈕時,通過「close-main-window」通道的send()方法發(fā)送消息。 這里還有個小問題,如果不注意會卡住你,我們已經討論過--可拖動區(qū)域的可點擊性。index.css需要把關閉按鈕定義成不可拖動: .settings {
...
-webkit-app-region: no-drag;
}
就這樣,現在可以點擊關閉按鈕關閉我們的應用了。因為要監(jiān)聽事件或傳遞參數,通過ipc模塊通信比較復雜。我們后面會看到一個傳遞參數的例子。 用全局快捷鍵播放聲音請切換到名為03-closable-sound-machine的tag: 基礎的發(fā)聲器工作順利,但是我們有一個易用性問題--如果發(fā)聲器一定需要切到應用窗口,再點擊才能播放,這個發(fā)聲器有什么用? 這時我們需要的就是全局快捷鍵。Electron提供一個全局快捷鍵模塊,允許你監(jiān)聽自定義的鍵盤組合并做出反應。鍵盤組合也被叫做加速器,是一系列鍵盤點擊組成的字符串(例如 “Ctrl+Shift+1”)。
既然我們想要捕捉一個原生GUI事件(全局快捷鍵),然后在應用窗口做出反應(播放聲音),我們仍用ipc模塊在主進程和渲染進程之間通信。 在深入到代碼層面前,有兩件事要考慮:
記住這些,現在用下面的代碼來修改我們的main.js文件: var globalShortcut = require('global-shortcut');
app.on('ready', function() {
... // existing code from earlier
globalShortcut.register('ctrl+shift+1', function () {
mainWindow.webContents.send('global-shortcut', 0);
});
globalShortcut.register('ctrl+shift+2', function () {
mainWindow.webContents.send('global-shortcut', 1);
});
});
首先,我們需要引入global-shortcut模塊。然后當我們的程序加載完成,我們注冊兩個快捷鍵--一個響應Ctrl,Shift,1組合鍵,另一個響應Ctrl,Shift,2組合鍵。兩者都會通過「global-shortcut」通道發(fā)送一條帶一個參數的消息。我們用這些參數來播放相應的聲音。在index.js中加入以下代碼: ipc.on('global-shortcut', function (arg) {
var event = new MouseEvent('click');
soundButtons[arg].dispatchEvent(event);
});
為了方便,我們會模擬一次按鈕點擊,用我們創(chuàng)建的soundButton選擇器給按鈕綁定一個播放聲音。當收到帶有參數1的消息,我們在soundButton[1]元素上模擬一次鼠標點擊(在正式環(huán)境的應用,你應該封裝播放聲音的代碼,并執(zhí)行它)。 在新的窗口修改鍵位配置切換到名為04-global-shortcuts-bound的tag: 系統(tǒng)同時運行很多應用程序,我們預想的快捷鍵可能已經被占用了。這正是我們將要新建一個設置窗口,保存我們想要的鍵位修改的原因。 要實現這個目標,我們需要:
設置按鈕和設置窗口 類似關閉主窗口,當點擊設置按鈕時我們通過通道從index.js發(fā)送消息。將下面代碼加入index.js: var settingsEl = document.querySelector('.settings');
settingsEl.addEventListener('click', function () {
ipc.send('open-settings-window');
});
點擊設置按鈕后,通道「open-settings-window」會發(fā)送一條消息到主進程。main.js現在需要做出響應,新建一個窗口,將下面代碼插入main.js: var settingsWindow = null;
ipc.on('open-settings-window', function () {
if (settingsWindow) {
return;
}
settingsWindow = new BrowserWindow({
frame: false,
height: 200,
resizable: false,
width: 200
});
settingsWindow.loadUrl('file://' + __dirname + '/app/settings.html');
settingsWindow.on('closed', function () {
settingsWindow = null;
});
});
沒有什么新概念,我們會像打開主窗口一樣打開新的設置窗口。不同之處是要先檢查設置窗口是不是已經被打開,以防重復打開。 打開后,需要一種方法關閉設置窗口。同樣的,我們會通過通道發(fā)送一條消息,但這次消息是從settings.js發(fā)出,將下面代碼寫入setting.js: 'use strict';
var ipc = require('ipc');
var closeEl = document.querySelector('.close');
closeEl.addEventListener('click', function (e) {
ipc.send('close-settings-window');
});
在main.js里面監(jiān)聽那個通道,代碼如下: ipc.on('close-settings-window', function () {
if (settingsWindow) {
settingsWindow.close();
}
});
我們的設置窗口就完成了。 保存和讀取用戶的設置 切換到名為05-settings-window-working的tag: 與設置窗口交互,保存設置,再讀取到我們的應用的過程大致是這樣的:
我們可以簡單的保存和讀取main.js中的設置,但模塊把邏輯抽象出來,以便我們可以在不同的地方引用,這看看起來更好。 Working with a JSON configuration 那就是我們新建configuration.js的原因。Node.js用CommonJS模塊規(guī)范,這意味著你只可以暴露你的API,而其他文件或方法會引用API提供的方法。
為了讓保存和讀取更簡便,使用nconf模塊,它已經為我們抽象出讀取和寫入JSON文件的方法,非常符合我們的需求。但首先,我們要在CLI中執(zhí)行下面的命令將它引入項目中: npm install --save nconf npm將nconf模塊作為應用的依賴安裝。在我們打包應用給終端用戶時(相對用save-dev參數會只在開發(fā)環(huán)境中引入模塊)將被引入和使用。 configuration.js文件非常的簡單,在項目根目錄下新建configuration.js文件,寫入代碼: 'use strict';
var nconf = require('nconf').file({file: getUserHome() + '/sound-machine-config.json'});
function saveSettings(settingKey, settingValue) {
nconf.set(settingKey, settingValue);
nconf.save();
}
function readSettings(settingKey) {
nconf.load();
return nconf.get(settingKey);
}
function getUserHome() {
return process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'];
}
module.exports = {
saveSettings: saveSettings,
readSettings: readSettings
};
nconf只需要知道你的設置要保存到哪里,這里我們設置為客戶的主文件夾和一個文件名。獲取用戶的主文件夾非常簡單,只需要區(qū)別不同系統(tǒng)調用Node.js(process.env)(如用getUserHome()方法)。 通過nconf的內建方法來保存或讀取設置(set()方法保存,get()方法讀取,用save()和load()方法進行文件操作),用符合CommonJS規(guī)范的module.exports語法來導出API。 初始化修改的快捷鍵 在我們進行設置的交互之前,應初始化設置,以防我們先啟動應用丟失設置信息。我們把變更鍵保存在一個數組中,數組以「shortcutKeys」為鍵,在main.js里初始化,我們首先要引用configuration模塊: 'use strict';
var configuration = require('./configuration');
app.on('ready', function () {
if (!configuration.readSettings('shortcutKeys')) {
configuration.saveSettings('shortcutKeys', ['ctrl', 'shift']);
}
...
}
嘗試讀取「shortcutKeys」鍵對應的值,如果讀取不到,就設置一個初始值。 現在要重寫main.js中的全局快捷鍵,這個方法可以在后面更新設置的時候直接調用。 去掉原來在main.js中注冊快捷鍵的方法,改成: app.on('ready', function () {
...
setGlobalShortcuts();
}
function setGlobalShortcuts() {
globalShortcut.unregisterAll();
var shortcutKeysSetting = configuration.readSettings('shortcutKeys');
var shortcutPrefix = shortcutKeysSetting.length === 0 ? '' : shortcutKeysSetting.join('+') + '+';
globalShortcut.register(shortcutPrefix + '1', function () {
mainWindow.webContents.send('global-shortcut', 0);
});
globalShortcut.register(shortcutPrefix + '2', function () {
mainWindow.webContents.send('global-shortcut', 1);
});
}
方法會重置全局快捷鍵,現在我們可以設置新的快捷鍵,從設置文件讀取變更鍵數組,轉換類加速器規(guī)則字符串,再注冊全局快捷鍵。 與設置窗口交互 回到settings.js,我們要綁定click事件來修改我們的全局快捷鍵。首先,我們遍歷所有勾選的復選框(從configuration模塊中讀?。?/p> var configuration = require('../configuration.js');
var modifierCheckboxes = document.querySelectorAll('.global-shortcut');
for (var i = 0; i < modifierCheckboxes.length; i++) {
var shortcutKeys = configuration.readSettings('shortcutKeys');
var modifierKey = modifierCheckboxes[i].attributes['data-modifier-key'].value;
modifierCheckboxes[i].checked = shortcutKeys.indexOf(modifierKey) !== -1;
... // Binding of clicks comes here
}
現在我們要給復選框綁定行為。記得設置窗口(渲染進程)不能改動GUI綁定。這意味著我們需要從setting.js通過ipc發(fā)送消息(后面會處理消息): for (var i = 0; i < modifierCheckboxes.length; i++) {
...
modifierCheckboxes[i].addEventListener('click', function (e) {
bindModifierCheckboxes(e);
});
}
function bindModifierCheckboxes(e) {
var shortcutKeys = configuration.readSettings('shortcutKeys');
var modifierKey = e.target.attributes['data-modifier-key'].value;
if (shortcutKeys.indexOf(modifierKey) !== -1) {
var shortcutKeyIndex = shortcutKeys.indexOf(modifierKey);
shortcutKeys.splice(shortcutKeyIndex, 1);
}
else {
shortcutKeys.push(modifierKey);
}
configuration.saveSettings('shortcutKeys', shortcutKeys);
ipc.send('set-global-shortcuts');
}
我們遍歷了所有的復選框,綁定click事件,在每次點擊時判斷是否含有變更鍵。然后根據結果,修改數組,保存結果到設置,再給主進程發(fā)送消息,它會更新我們的全局快捷鍵。 下面要在main.js里的設置「set-global-shortcuts」這個ipc通道來更新我們的全局快捷鍵: ipc.on('set-global-shortcuts', function () {
setGlobalShortcuts();
});
很簡單,像這樣,我們的全局快捷鍵就配置好了! 菜單上有什么?切換到名為06-shortcuts-configurable的tag: 對桌面應用來說,另一個重要的概念就是菜單欄。分為上下文菜單(右擊菜單),托盤菜單(綁定到托盤圖標),應用菜單(在OS X上)等多種。
在本指南中,我們將添加一個綁定菜單的托盤圖標。我們也會利用這次機會探索另一種進程間通信--remote模塊。 remote模塊實現從渲染進程向主進程發(fā)送RPC式調用。你引入模塊,在渲染進程操作,方法在主進程被初始化,你調用的方法都在主進程被執(zhí)行。實際中,這意味著你在index.js遠程請求原生的GUI模塊,調用它們的方法,都會在main.js中執(zhí)行。你可以在index.js里引入BrowserWindow模塊,初始化一個瀏覽器窗口。背后的原理是,異步調用新的瀏覽器窗口的主進程。
現在我們創(chuàng)建一個菜單,并把它綁定到托盤圖標,在index.js中加入下面代碼: var remote = require('remote');
var Tray = remote.require('tray');
var Menu = remote.require('menu');
var path = require('path');
var trayIcon = null;
if (process.platform === 'darwin') {
trayIcon = new Tray(path.join(__dirname, 'img/tray-iconTemplate.png'));
}
else {
trayIcon = new Tray(path.join(__dirname, 'img/tray-icon-alt.png'));
}
var trayMenuTemplate = [
{
label: 'Sound machine',
enabled: false
},
{
label: 'Settings',
click: function () {
ipc.send('open-settings-window');
}
},
{
label: 'Quit',
click: function () {
ipc.send('close-main-window');
}
}
];
var trayMenu = Menu.buildFromTemplate(trayMenuTemplate);
trayIcon.setContextMenu(trayMenu);
原生的GUI模塊(菜單和托盤)的方法會被遠程調用,是很安全的。 把圖標定義成托盤圖標。OS X支持圖像模板(依照慣例,圖像的文件名以「Template」結尾,可以被當做一個模板圖像),這讓使用深淺色主題變得很容易。其他系統(tǒng)用常規(guī)的圖標。 在Electron中有很多種方法創(chuàng)建菜單。我們的方法是創(chuàng)建一個菜單模板(一個包含菜單項的簡單數組),用那個模板創(chuàng)建菜單。最后,綁定新的菜單到托盤圖標。 打包你的應用切換到名為07-ready-for-packaging的tag: 如果不能讓人們下載使用,這樣的應用有什么意義?
用「electron-packager」為所有系統(tǒng)打包你的應用很簡單。簡單來說,「electron-packager」幫你完成所有用Electron打包你應用的工作,最終生成你要發(fā)布的平臺的安裝包。 它可以作為CLI應用或構建過程的一部分,更復雜的構建情況不在本文所涉及范圍內,但我們如果能用打包腳本,會使打包更簡單。用「electron-packager」比較麻煩,打包應用的基本命令是: electron-packager <location of project> <name of project> <platform> <architecture> <electron version> <optional options> 其中,
第一次打包用時比較久,因為要下載平臺的二進制文件,隨后的打包將會快的多。 我(在Mac系統(tǒng))打包發(fā)聲器應用的命令是: electron-packager ~/Projects/sound-machine SoundMachine --all --version=0.30.2 --out=~/Desktop --overwrite --icon=~/Projects/sound-machine/app/img/app-icon.icns 命令的選項理解起來都比較簡單。為了獲得精美的圖標,你首先要找一款類似這個軟件可以把PNG文件轉換到這些格式的工具,把它轉換成.icns格式(Mac用)或者.ico格式(Window用)。如果在非Windows系統(tǒng)給Windows平臺的應用打包,你需要安裝wine(Mac用戶用brew,Linux用戶用apt-get)。 每次都打這么長的命令很不方便,可以在package.json中加另一個腳本。首先,把electron-packager作為開發(fā)依賴安裝: npm install --save-dev electron-packager 現在我們可以在package.json中添加腳本: "scripts": {
"start": "electron .",
"package": "electron-packager ./ SoundMachine --all --out ~/Desktop/SoundMachine --version 0.30.2 --overwrite --icon=./app/img/app-icon.icns"
}在命令行里執(zhí)行下面的命令: npm run-script package 這個打包命令會啟動electron-packager,在當前目錄下找到目標應用文件,打包,保存到桌面。如果你用的是Windows系統(tǒng),需要修改腳本,不過改動很小。 當前狀態(tài)的發(fā)聲器,最后打包后大小高達100MB。別擔心,可以把它壓縮到不到一半的容量。 如果你想要更進一步,可以嘗試electron-builder,它用electron-packager生成的打包好的文件,可以生成自動安裝包。 可以添加的其他功能應用已經打包好,準備就緒。你也可以添加自己想要的功能。 這是一些想法:
挑戰(zhàn)來了--嘗試抽取出發(fā)聲器瀏覽器窗口的邏輯,用這些邏輯在瀏覽器中創(chuàng)建web頁面,實現相同的發(fā)聲器。一個代碼庫--兩個產品(桌面應用和web應用),超棒! 深入Electron我們只接觸到了Electron比較淺顯知識。實際上,實現如查看主機電源選項或在界面上顯示多種信息都很簡單。這些功能已經內建好,請查閱Electronde API文檔。 Electron的API文檔只是Electron在Github上資料庫的一小部分,其他文件夾也值得一看。 Sindre Sorhus正在維護超酷的Electron資源列表,你可以在列表中找到很多很酷的項目,也有Electron應用構架方面很好的總結,可以學習之后重構我們的代碼。 最后,Electron是基于io.js(將合并回Node.js)的,兼容絕大部分的Node.js模塊,可以用來擴展你的應用,查看來獲取你需要的信息。 這就完了?當然不是。 現在是時候來創(chuàng)建更復雜的應用了。在本指南中,我沒有選擇用更多函數庫和構建工具,只強調了重要的概念。你也可以用ES6或Typescript來寫你的應用,使用Angular或React框架,用gulp或Grunt來簡化你的構建過程。 為什么不用你最喜歡的語言,框架和構建工具,配合Flickr API,node-flickrapi創(chuàng)建一個Flickr桌面同步應用呢?或者用Google官方的Node.js函數庫創(chuàng)建一個Gmail客戶端? 選一個吸引你的想法,創(chuàng)建一個資料庫,開始做吧。 |
|
|