NW.js 是一個(gè)使用 Web 技術(shù)創(chuàng)建本地應(yīng)用的框架,如 HTML、JavaScript 和 CSS。簡(jiǎn)單地說(shuō),當(dāng)你在使用普通的流程開(kāi)發(fā)一個(gè) Web 應(yīng)用時(shí),開(kāi)發(fā)完成后,運(yùn)行一個(gè)生成器,將所有東西編譯成一個(gè)本地應(yīng)用,它會(huì)像一個(gè)瀏覽器一樣運(yùn)行你的 Web 應(yīng)用。這種應(yīng)用就被稱(chēng)為“Hybrid 應(yīng)用(一種混合本地編程和 Web 編程技術(shù)的應(yīng)用)”。
Hybrid 應(yīng)用的偉大之處,不僅在于它可以使用你熟悉的語(yǔ)言(HTML、JavaScript 和 CSS)來(lái)開(kāi)發(fā),還因?yàn)樗绕胀ǖ?Web 應(yīng)用更有優(yōu)越性:
控制瀏覽器和瀏覽器版本(你知道你的應(yīng)用是調(diào)用的什么瀏覽器)。NW.js hybrid 應(yīng)用使用 Chromium 來(lái)顯示— 這是一種開(kāi)源瀏覽器,也是 Google Chrome(谷歌瀏覽器)的核心。因此,能在 Chrome 中運(yùn)行的應(yīng)用也能在 NW.js 中運(yùn)行。
控制視窗。例如,你可以定義一個(gè)固定大小,或者最小化/最大化的視窗。
對(duì)本地文件的訪問(wèn)不會(huì)受同源策略的約束。如果你想在瀏覽器通過(guò) XMLHttpRequest 打開(kāi)一個(gè)不在相同目錄的本地文件,請(qǐng)求會(huì)阻止。而 NW.js 應(yīng)用中關(guān)閉了這樣的行為。
它們也提供了 API,帶來(lái)如下優(yōu)點(diǎn):
整合 Node.js
訪問(wèn)剪貼板
訪問(wèn)文件系統(tǒng)
訪問(wèn)硬件(比如獲取打印機(jī)列表)
托盤(pán)圖標(biāo)
自定義文件選擇對(duì)話框
整合 shell(在默認(rèn)的資源管理器或?yàn)g覽器中打開(kāi)文件或 URL)
自定義主窗口的選項(xiàng)(關(guān)閉按鈕、菜單欄)和上下文菜單
設(shè)置和獲取綻放等級(jí)。
看起來(lái)不錯(cuò)?那讓我們開(kāi)始吧。在這篇文章中,我們會(huì)通過(guò)練習(xí)熟悉 NW.js,并學(xué)習(xí)如何創(chuàng)建一個(gè) Hybrid 應(yīng)用。用于這篇文章的示例應(yīng)用已經(jīng)在 GitHub 上準(zhǔn)備好了。
首先要說(shuō)的是,NW.js 并不是唯一的 Bybrid 應(yīng)用框架。另一個(gè)這樣的框架叫 Electron。它誕生于 2013年,比 NW.js 晚兩年,不過(guò)因?yàn)樗鼇?lái)自于 GitHub,很快就被大家所認(rèn)識(shí)?,F(xiàn)在你可能對(duì)它們之間的區(qū)別感興趣。這里列舉了與 Electron 相比,NW.js 的優(yōu)勢(shì):
支持 chrome.* API。這些 API 可用于與瀏覽器交互。(你可以在 NW.js 文檔中找到更多相關(guān)的信息)
支持 Chrome 應(yīng)用。Chrome應(yīng)用是使用 Web 語(yǔ)句編寫(xiě)并打包的應(yīng)用。(更多信息請(qǐng)參閱 Chrome 開(kāi)發(fā)者文檔。)這些應(yīng)用與 NW.js 不同,因?yàn)樗鼈儧](méi)有整合 Node.js,而且通過(guò) Chrome Web Store 發(fā)布。(Chrominum 會(huì)在2018年8月取消對(duì)它的支持(參閱他們的博客)。不過(guò)因?yàn)?a rel="nofollow">這篇文章所說(shuō)的原因,NW.js 仍然會(huì)支持 Chrome 應(yīng)用。)
支持 NaCl (Native Client,本地客戶端) 和 PNaCl (可移植的本地客戶端) 應(yīng)用。它們致力于性能,因此使用 C 和 C++ 編寫(xiě)。(參閱這篇教程了解如何在 NW.js 中使用它們。)
擁有 V8 的映像源碼保護(hù),保護(hù)你的應(yīng)用程序源碼。使用 nwjc 工具可以將你的代碼編譯為本地代碼。(更多信息參考這篇文章。)
擁有一個(gè)內(nèi)建的 PDF 閱讀器。
允許打印預(yù)覽。
支持 Node.js 整合 Web Workers。這用于編寫(xiě)多線程應(yīng)用。
不過(guò),Electron 也有值得一提的優(yōu)點(diǎn):
內(nèi)建自動(dòng)更新(你可以在這個(gè)事項(xiàng)里找到關(guān)于 NW.js 的自動(dòng)更新)。
自動(dòng)向遠(yuǎn)程服務(wù)器報(bào)告程序崩潰。NW.js 只會(huì)把錯(cuò)誤信息寫(xiě)入一個(gè)本地文件,需要手工提交。
還有一個(gè)重要的區(qū)別。NW.js 應(yīng)用的入口是一個(gè) HTML 文件中的 Form。這個(gè) HTML 文件會(huì)直接在 GUI 中打開(kāi)。
另一方面,Electron 應(yīng)用使用一個(gè) JavaScript 文件作為入口。這個(gè) JavaScript 文件由另一個(gè)主進(jìn)程打開(kāi),然后由它在 GUI 中打開(kāi) HTML。這樣的話,理論上你可以不通過(guò) GUI 運(yùn)行 Electron 應(yīng)用。同樣的道理,關(guān)閉 GUI 不會(huì)關(guān)閉主進(jìn)程;你需要調(diào)用一個(gè) API 來(lái)終止主進(jìn)程。
雖然 Electron 不使用 GUI 來(lái)啟動(dòng) JavaScript 寫(xiě)的桌面應(yīng)用,但 NW.js 應(yīng)用卻更容易建立示基于顯示 HTML 的應(yīng)用。
注意: 如果你確實(shí)關(guān)心 Electron 的優(yōu)勢(shì),看看 SitePoint 最新的文章使用 Electon創(chuàng)建桌面應(yīng)用。
來(lái),開(kāi)始創(chuàng)建我們的應(yīng)用,稍后我們會(huì)把它編譯成本地應(yīng)用。因?yàn)閯?chuàng)建 Web 應(yīng)用的方式多種多樣——使用不同的 JS 語(yǔ)言(TypeScript、CoffeScript 等),模塊加載器(RequireJS、webpack、SystemJS 等),框架(AngularJS、React、Vuew.js 等)和(樣式表)預(yù)處理器(SCSS、LESS、Haml 等)——每個(gè)人都有自己的偏好,我們只使用基本的技術(shù),HTML、CSS 和 JS(ES6 標(biāo)準(zhǔn))。
NW.js 沒(méi)有樣板 (初始項(xiàng)目) 來(lái)完成初始的設(shè)置。它們都通過(guò)特定的框架、模塊加載器或預(yù)處理器來(lái)創(chuàng)建。然而,我們從頭開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 NW.js 應(yīng)用程序。它會(huì)比較易懂,之后你可以很容易按自己的需求定制,或者把它變成樣板。
首先,我們需要?jiǎng)?chuàng)建項(xiàng)的目錄結(jié)構(gòu)和文件:
nw.js-example/├── src/│ ├── app/│ │ └── main.js│ ├── assets/│ │ └── icon.png│ ├── styles/│ │ └── common.css│ ├── views/│ │ └── main.html│ └── package.json└── package.json
說(shuō)明:
src/ 應(yīng)用程序源文件。
src/app/ JavaScript 文件。
src/assets/ 圖片,在我們的例子中只有 icon.png 文件,它將作為一個(gè)窗口圖標(biāo)來(lái)顯示。
src/styles/ 通常包含 SCSS 或 LESS 文件,在我們的例子中,僅僅是一個(gè)簡(jiǎn)單的 CSS 文件。
src/views/ 包含 HTML 視圖文件。
src/package.json NW.js 應(yīng)用程序清單文件(參考清單文件格式),我們也可以在這個(gè)文件中為應(yīng)用程序指定特殊的依賴項(xiàng)。
package.json 是一個(gè) npm 包文件,我們用它來(lái)構(gòu)建工作流,也可以指定特殊的依賴,這在實(shí)際的 NW.js 應(yīng)用程序中不是必須的,例如根據(jù)依賴構(gòu)建。
現(xiàn)在,我們已經(jīng)創(chuàng)建了項(xiàng)目結(jié)構(gòu)和文件,可以從 NW.js 的清單文件 src/package.json 開(kāi)始了。根據(jù)文檔,這個(gè)文件基本上僅需要兩個(gè)屬性:name,一個(gè)應(yīng)用程序名稱(chēng),和 main,一個(gè)作為入口的 HTML 文件的路徑。但是我們需要添加更多信息,如窗口的圖標(biāo)的路徑,以及最小寬度和高度,以確保用戶不會(huì)看到任何奇怪的事情:
{ "name":"nw.js-example", "main":"views/main.html", "window":{ "min_width":400, "min_height":400, "icon":"assets/icon.png" }}
就是這樣!應(yīng)用程序在開(kāi)始運(yùn)行后,將打開(kāi) src/views/main.html,main 的路徑是以清單文件(manifest file)為參考的相對(duì)路徑。
在這個(gè)時(shí)候我們可以編寫(xiě)一個(gè)待辦事項(xiàng)程序。但是我們要專(zhuān)注于 NW.js 和它本身的功能。因此,我更傾向讓你自己來(lái)決定我們的應(yīng)用程序的功能。 我在 GitHub 上創(chuàng)建了一個(gè)示例項(xiàng)目 NW.js-示例來(lái)演示幾個(gè) NW.js功 能,例如,Node.js 集成和剪貼板訪問(wèn)。 您隨時(shí)可以在應(yīng)用程序中用它來(lái)測(cè)試和研究。當(dāng)然你也可以使用其他的功能。
不管你作何決定,你都必須要?jiǎng)?chuàng)建 thesrc/views/main.html 文件。因?yàn)樗俏覀儜?yīng)用程序的入口點(diǎn)。文件內(nèi)容如下:
<!doctype html><html><head> <meta charset="utf-8"> <title>NW.js-example | main</title> <link rel="stylesheet" href="../styles/common.css"></head><body> <h1>Hello World :-)</h1> <script src="../app/main.js"></script></body></html>
在真正的應(yīng)用程序中,你可能有個(gè)多個(gè)其他的視圖文件并用 Ajax 加載它們。
為了簡(jiǎn)單起見(jiàn),您還可以創(chuàng)建本地超鏈接并引用其他 HTML 文件。 例如:
<a href="something.html">Something</a>
然后,在 src / views / 中創(chuàng)建 something.html 文件。這是我使用的項(xiàng)目示例。
現(xiàn)在我們創(chuàng)建了 NW.js 項(xiàng)目,包括清單和主視圖。最終我們需要一個(gè)方法直接在開(kāi)發(fā)中運(yùn)行 NW.js,并通過(guò)構(gòu)建過(guò)程生成可運(yùn)行于多個(gè)系統(tǒng)的本地應(yīng)用。
為達(dá)此目的,我們需要兩個(gè)包:
nw (開(kāi)發(fā))
nw-builder (生產(chǎn))
既然它們與我們的應(yīng)用功能無(wú)關(guān)(使用它們只是為了開(kāi)發(fā)和生產(chǎn)目的),我們?cè)诘诙€(gè),即放在根目錄下的 package.json 中創(chuàng)建 devDependencies 配置,加入它們。這個(gè)配置與必須的 name 和 version 字段并列:
{ "name":"nw.js-example", "version":"1.0.0", "devDependencies":{ "nw":"^0.18.2", "nw-builder":"^3.1.2" }}
現(xiàn)在只需要在項(xiàng)目的根目錄下運(yùn)行下面的命令來(lái)安裝 devDependencies:
$ npm install
搞定!可以構(gòu)建了。
我們?cè)?package.json 中加入 npm 腳本來(lái)讓打包變得簡(jiǎn)單。npm 腳本在右邊定義要運(yùn)行的 CLI 命令,在左邊定義它的快捷方式,然后允許我們通過(guò) npm run 來(lái)運(yùn)行指定的快捷方式。現(xiàn)在添加兩個(gè)腳本,一個(gè)用于開(kāi)發(fā),一個(gè)用于生產(chǎn):
{ "name":"nw.js-example", "version":"1.0.0", "devDependencies":{ "nw":"^0.18.2", "nw-builder":"^3.1.2" }, "scripts":{ "dev":"nw src/", "prod":"nwbuild --platforms win32,win64,osx64,linux32,linux64 --buildDir dist/ src/" }}
直接運(yùn)行 NW.js 應(yīng)用,只需要這個(gè)命令:
$ npm run dev
這個(gè)快捷方式會(huì)調(diào)用我們定義在 script 下 dev 對(duì)應(yīng)的命令以使用 nw 包。開(kāi)發(fā)機(jī)器上直接打開(kāi)一個(gè)新窗口,顯示著 src/views/main.html。
要進(jìn)行生產(chǎn)構(gòu)建需要使用 nw-builder,它支持針對(duì) Windows、Linux 和 macOS 進(jìn)行輸出。在我們的例子中,我們會(huì)針對(duì)所有這些平臺(tái)打包,并且包含32位和64位版本。對(duì)于 macOS 來(lái)說(shuō),目前只能在以前的模式下構(gòu)建 32位的版本。(參閱 GitHub 上的這個(gè) issue。)所以,我們只構(gòu)建64位版本。
運(yùn)行如下命令進(jìn)行生產(chǎn)構(gòu)建:
$ npm run prod
和直接運(yùn)行 NW.js 一樣,它也使用我們定義在 scripts 中的 CLI 命令。
然后得等一會(huì)兒 …
完成之后,看看你的 dist/ 目錄,就像這樣:
dist/└── nw.js-example/ ├── linux32/ ├── linux64/ ├── osx64/ ├── win32/ └── win64/
非常好,我們差不多完成了!
既然 NW.js 是基于 Chrominum 的,那么它的手工測(cè)試和 Chrome 一樣簡(jiǎn)單。如果你遇到一個(gè)BUG——可視效果或者功能性的——你都可以通過(guò)快捷鍵F12或者下面的程序打開(kāi)開(kāi)發(fā)者工具:
nw.Window.get().showDevTools();
注意這需要SDK build flavor[譯者注:flavor 可以理解為插件或者功能擴(kuò)展]。如果你想在生產(chǎn)構(gòu)建的時(shí)候去掉開(kāi)發(fā)者工具,你可以使用另一個(gè)flavor或者直接阻止F12事件。
自動(dòng)化的單元測(cè)試(幸好有你)廣泛用于確保不同的實(shí)現(xiàn)能正確工作,這避免了總是進(jìn)行手工測(cè)試。
如果你的應(yīng)用不使用 NW.js API 提供的特有方法,理論上來(lái)說(shuō)你可以停留在一般的Web應(yīng)用工作流上——例如,把Karma作為一個(gè)運(yùn)行器與作為框架的Jasmine聯(lián)合使用。
但是如果你使用 NW.js API 特有的方法,你需要在 NW.js 應(yīng)用中運(yùn)行測(cè)試,這才能確保使用到的 API 方法存在。一種方法是使用 NW.js 的 Karma 啟動(dòng)器插件,比如 karma-nodewebkit-launcher。它和其它瀏覽器的 Karma 啟動(dòng)器插件一樣:在 NW.js 容器中打開(kāi)應(yīng)用并進(jìn)行檢查,完成之后再自動(dòng)關(guān)閉應(yīng)用。
不過(guò)既然 NW.js 不是 headless(就像 PhantomJS 那樣的)[譯者注:Headless 瀏覽器就是沒(méi)有 GUI 的瀏覽器], 它總是需要顯示出來(lái)。換句話說(shuō),它不能在純 CLI 服務(wù)器上運(yùn)行測(cè)試。幸運(yùn)的是,這種情況下可以使用 Xvfb 來(lái)模擬顯示。在 Jenkins[譯者注:一個(gè)持續(xù)集成引擎]中,你需要安裝 Xvfb 插件。更多信息參閱 GitHub 上的這個(gè)討論。
聯(lián)系客服