NW.js開發環境搭建
簡介:NW.js是什麼?
NW.js基於Chromium和Node.js。它使您可以直接從瀏覽器中調用Node.js代碼和模塊,也可以在應用程序中使用Web技術。此外,您可以輕鬆地將Web應用程序打包到本機應用程序。
1. 選擇Build Flavors SDK
NW.js支持各種構建樣式以減小應用程序大小。當前,NW.js支持以下構建風格:
- SDK風格(SDK):內置了對DevTools和NaCl插件的支持。SDK風味與0.13.0之前的版本具有相同的功能(適用於開發調試環境);
- 正常風格(Normal):是不帶DevTools和NaCl插件支持的最低版本(適用於發佈環境)。
建議您選擇SDK構建風格來開發應用程序,這使您可以使用DevTools調試應用程序。
2. 下載安裝NWJS
對於國內用戶,如果下載連接下載速度太慢的話,可以考慮的可選項是:
或者進入官網下載根據自己需要下載對應的版(我這裏下載的是mac版本的v0.44.5,osx-x64,normal
v0.44.5,osx-x64,sdk
-
下載後直接解壓,將
nwjs.app
拖到應用程序中(便於從桌面點擊圖標啓動)
-
設置別名和環境變量(以便從命令行可直接啓動)
#打開bash_profile環境變量配置文件 vim ~/.bash_profile #設置環境變量-nwjs的別名 alias nw="/Applications/nwjs.app/Contents/MacOS/nwjs" #應用環境變量 source ~/.bash_profile #命令行輸入nw 回車啓動即可(等同於桌面點擊圖標啓動) nw
啓動後如下如:
3. 創建第一個應用
-
創建package.json文件
通過node.js初始化一個項目(首先,確保你已經安裝好了
node.js
的環境),生成package.json
文件,package.json
是JSON 格式格式的配置文件.main
屬性定義了應用首頁, 如本例的"index.html"
.name
則定義了應用名稱. 具體查看 配置文件章節.#創建項目目錄 mkdir NWDemo #進入項目根目錄 cd NWDemo #執行初始化生成package.json npm init
package.json
{ "name": "test1", "version": "1.0.0", "description": "", "main": "index.html", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }
您可以將
"main"
屬性設置如"main.js"
的js文件. 該文件在應用啓動時默認不打開窗口並在後臺執行。 您可以稍後進行一些初始化並手動打開窗口。 例如:// 初始化你的應用程序之後 ... nw.Window.open('index.html', {}, function(win) {});
-
創建入口頁面
index.html
<!DOCTYPE html> <html> <head> <title>Hello World!</title> </head> <body> <h1>Hello World!</h1> </body> </html>
4. 運行應用
進入我們創建的項目中,執行nw .
運行當前應用(nw .
是NW.js執行文件)
- Windows系統中是
nw.exe
; - Linux系統中是
nw
; - Mac系統中是
nwjs.app/Contents/MacOS/nwjs
;
#進入項目根目錄
cd NWDemo
#運行
nw .
【注意】
Windows系統中 , 可拖拽包含 package.json
的文件夾至 nw.exe
直接運行應用。
5. 打包應用
可以使用以下工具自動完成打包NW.js應用進行發佈 .
- nwjs-builder-phoenix (推薦:用於爲Windows,macOS和Linux構建和打包可分發的NW.js應用程序。)
- nw-builder
或者可以使用以下步驟手動構建應用 ,具體步驟參考手動構建應用
這裏使用nwjs-builder-phoenix
構建方式詳細使用參見github
6. APIs
詳細使用參見地址
擴展
1. 配置文件(package.json)
{
"name": "nw-demo", #應用名稱,應確保該字段內容全局唯一性
"main": "index.html", #應用起始頁
"nodejs":true, #是否支持Node
"node-main":"xxxx", #指定Node.js腳本文件路徑並且它將在加載DOM窗口之前啓動Node環境時執行
"domain":"xxxx", #指定主機域名
"single-instance":false, #是否以單實例運行(false則允許應用多開,默認 true)
"bg-script":"xxxx", #應用啓動時執行的後臺腳本
/**
*窗體樣式控制(窗口子字段默認情況被繼承到使用 window.open()或 <a target="_blank">打開的子窗口
*未繼承子字段將被設置爲打開窗口時的默認值)
*列表如下:
* fullscreen -> false
* kiosk -> false
* position -> null
* resizable -> true
* show -> true
*/
"window":{
"id":"xxx", #內含窗口尺寸與位置的狀態的窗口ID,打開同ID的窗口時會還原該狀態
"title":"xxx", #NW.js創建的窗口標題 . 在應用啓動時顯示的標題信息
"width":200, #窗口寬高
"height":200, #窗口高
"toolbar":true, #是否顯示導航欄中的工具條
"icon":"xxxx", #窗口圖標路徑
"position":"xxx", #窗口位置:默認 null(不固定) , center(屏幕居中) , mouse(鼠標所在位置)
"min_width":100, #窗口最小寬高
"min_height":100,
"max_width":500, #窗口最大寬高
"max_height":500,
"as_desktopLinux":false, #X11環境下,作爲桌面背景顯示(true顯示, false不顯示)
"resizable":true, #是否可調整窗口大小(在OS X上將該屬性設置爲 false,並將frame設置true,
#用戶還是可以將窗口全屏顯示。只有將全屏也設置爲 false纔可禁用全屏控件。
"always_on_top":false, #是否允許窗口始終置頂(在其餘窗口之上,true允許, false不允許)
"visible_on_all_workspaces Mac & Linux",: #支持多工作區的系統(如Mac & Linux)中,將窗口同時顯示
#在所有工作區中(true允許, false不允許)
"fullscreen":false, #是否允許窗口全屏(窗體和全屏框架(frame)應當一致,窗口設置爲 false時,
#則全屏框架(frame)不應設爲 true,避免窗體將阻止鼠標獲取屏幕邊緣)
"frame":true, #窗口是否爲框架(窗體和全屏框架應當一致,窗口設置爲 false時,則全屏框架
#不應設爲 true,避免窗體將阻止鼠標獲取屏幕邊緣.)
"show_in_taskbar":true, #是否允許顯示在任務欄或停靠欄中(true允許, false不允許,默認 true)
"show":true, #啓動時是否顯示應用(true顯示, false不顯示)
"kiosk":true, #是否使用 Kiosk模式(該模式即應用將全屏並阻止用戶離開(關閉)應用,比如常見
#的公共觸摸屏演示,銀行排隊取票機潔界面,直到頁面返回響應)
"transparent":false, #窗口是否透明(true允許, false不允許,默認 false)
#窗口的透明度由CSS中的背景透明值控制,
#使用命令行參數 --disable-transparency 可完全禁止透明功能.
#使用命令行參數 --disable-gpu 禁用GPU後,可實現透明窗體的穿透點擊
},
/**
*WebKit特性控制
*/
"webkit":{
"double_tap_to_zoom_enabled":false, #是否啓用兩指縮放功能(true允許, false不允許,默認 false)
"plugin":true, #是否可加載擴展插件,比如Flash插件(true允許, false不允許,默認 true),
},
"user-agent":"xxx", #重寫應用請求頁面中的 User-Agent信息
#以下變量內容可以動態設置 User-Agent內容:
#%name: 替換配置文件中的name字段 .
#%ver: 替換配置文件中的version字段 .
#%nwver: 替換NW.js版本 .
#%webkit_ver: 替換WebKit引擎版本 .
#%osinfo: 替換系統以及CPU信息 .
"chromium-args":"xxx", #分發應用時自定義chromium命令行參數至應用(想要禁用GPU加速視頻顯示,
#只需添加添加參數 "chromium-args" : "--disable-accelerated-
#video".)
"crash_report_url":"xxx", #應用崩潰時,崩潰轉存報告將被髮送到設定的服務器
"inject_js_start":"xxx", #CSS文件執行之後 , 其他DOM或腳本運行之前 , 執行的JavaScript代碼
"inject_js_end":"xxx", #頁面document對象加載之後 , 觸發 onload之前 , 執行的JavaScript
#代碼 . 主要作爲新窗口中 Window.open()的參數執行JavaScript代碼
"additional_trust_anchors", #證書作爲附加可用的根證書使用 , 允許連接自簽名證書或者CA簽發機構頒發
#證書的服務(Array - 數多個PEM編碼的證書組成的數組)
"dom_storage_quota":500, #Mb爲單位的DOM存數限制數量(建議設置爲期望值的兩倍)
/**
*其他字段
*/
"description":"xxx" #配置的描述說明,以.結束
"version":1.0, #應用版本信息
"maintainers":[{ #維護成員
"name": "Bill Bloggs",
"email": "[email protected]",
"web": "http://www.bblogmedia.com",
}],
"contributors":[{ #貢獻者,與維護成員格式一致,但首個應爲作者
"name": "xxx",
"email": "xxx",
"web": "xxx",
}],
"bugs":"xxx", #提交錯誤的網址,如mail或http地址
"licenses":"xxx", #許可證列表
"dependencies":"xxx", #必要依賴,組的順序非常重要,較前條目具有較高優先級.
"homepage":"xxx", #網站URL地址
}
2. 使用NW.js APIs
NW.js
中的APIs都被加載到nw
全局對象中,並能夠在javascript代碼中直接使用
舊版本加載
nw
方式爲require('nw.gui')
,返回nw
對象。
舉個🌰:通過NW.js
提供的API來創建應用此單(當用戶鼠標右擊打開上下文菜單顯示)
<html>
<head>
<title>創建上下文菜單</title>
</head>
<body>
<P>右擊顯示上下文菜單</P>
<script>
//1.通過NW.js 創建一個空菜單
let menu = new nw.Menu(); //js中可直接使用 nw 對象,已經被加載到全局對象中
//2.創建三個菜單項目
let menuItem1 = new nw.MenuItem({label:'菜單1',click:()=>{alert('選擇菜單1')}});
let menuItem2 = new nw.MenuItem({label:'菜單2',click:()=>{alert('選擇菜單2')}});
let menuItem3 = new nw.MenuItem({label:'菜單3',click:()=>{alert('選擇菜單3')}});
//3.將菜單項添加到菜單中
menu.append(menuItem1);
menu.append(menuItem2);
menu.append(menuItem3);
//4.對當前html文檔body標籤內的正文內容設置監聽事件
document.body.addEventListener('contextmenu',(ev)=>{
//阻止默認的彈出框
ev.preventDefault();
//彈出自定義的Menu菜單
menu.popup(ev.x,ev.y);
return false;
},false);
</script>
</body>
</html>
效果如下圖:
PS:
- 更多HTML DOM Event事件可參見**《HTML DOM 事件》**
3. 使用Node.js API(require
)
我們除了上述在DOM中直接使用NW.js
的APIS之外,還可以在DOM中直接調用node.js
代碼及模塊。這樣就可以通過NW.js輕鬆開發PC桌面應用了。
舉個🌰:利用Node.js的os
模塊擦護心操作系統的信息
<html>
<head>
<title>創建上下文菜單</title>
</head>
<body>
<!--這裏使用Flex彈性佈局-->
<div style="display:flex;flex-direction:row;justify-content:center;align-item:center">
<div style="flex:30%">您的當前系統爲:</div>
<div style="flex:70%" id='os_platform'></div>
</div>
<script>
//1.將node.js中os模塊腳本賦值給 mOS對象
let mOS = require('os');
//2.通過document的getElementById方法獲取對應的div對象,然後通過innerHTML修改div內容
document.getElementById('os_platform').innerHTML=mOS.platform();
</script>
</body>
</html>
PS:
- 您可以在NW.js中通過
npm
進行模塊的安裝然後使用。 - 詳細Flex佈局參見《Flex 佈局語法教程》
- 【注意】當使用
npm install
構建原生模塊時無法兼容 NW.js ABI. 如需使用則需要根據nw-gyp
進行NW.js的重建. 詳細參考使用原生模塊章節.
4. 開發工具與調試
開發工具只能在SDK構建方式中使用。
-
Open Developer Tools 開啓開發工具
Windows和Linux系統中使用快捷鍵
F12
開啓 , Mac系統中使用⌥⌘i
.此外 , windows系統中可以使用win.showDevTools()`開啓開發工具進行編程
-
調試Node.js模塊
NW.js默認進行獨立環境模式運行,調試Node.js模塊需要在引用中右鍵並選擇
Inspect Background Page
,當運行到Node.js模塊代碼時,調試器會自動聚焦並暫停運行。混合模式下,Node.js模塊可以在開發工具中直接進行調試。
-
遠程調試
使用命令行參數
--remote-debugging-port=port
指定端口進行監聽。例如,運行nw --remote-debugging-port=9222
,通過瀏覽器訪問http://localhost:9222/進行遠程調試
。 -
開發工具擴展
開發工具支持全部擴展,包括
ReactJS
等使用擴展需要在配置文件
manifest.json
添加chrome-extension://*
權限,並在nw運行時增加--load-extension=path/to/extension
命令行參數 . 擴展的文件從Chrome應用商店安裝之後拷貝到本地Chrome瀏覽器目錄下 .
5. NW.js中獨立環境和混合環境模式
NW.js基於Chrome應用構建,因此NW.js在開始運行時候,自動完成後臺加載。當創建一個窗口時,同時創建一個JavaScript環境。
NW.js中,默認情況下,Node.js模塊加載到後臺運行環境。
5.1 獨立環境模式
除瀏覽器環境,NW.js默認在後臺增加Node.js環境運行Node模塊,這樣NW.js同時擁有兩個JavaScript環境:
瀏覽器環境
和Node環境
5.1.1 瀏覽器環境
-
加載腳本
<script>
標籤- jQuery的
$.getScript()
- RequireJS 。
-
全局對象
-
創建新瀏覽器環境
創建新的frame或者窗口時 , 將得到一個新的瀏覽器環境
-
訪問
Node.js
和NW.js
的API將Node環境對象拷貝到瀏覽器環境中 , 這樣運行在瀏覽器環境的腳本能夠訪問Node.js對象
nw
– 所有NW對象 NW.js APIsglobal
– NW環境全局對象; 等同nw.global
require
– 加載Node.js模塊的方法; 等同nw.require()
, 但不支持通過require('nw.gui')
加載NW.js模塊.process
– Node.js模塊中process模塊; 等同nw.process
Buffer
– Node.js模塊中Buffer類
5.1.2 Node環境
-
加載腳本
- 通過Node.js的API中的
require()
加載腳本; - 通過配置文件中
node-main
加載腳本;
- 通過Node.js的API中的
-
全局對象
- js內建對象
- Node.js全局對象(如
__dirname
,process
,Buffer
等)
【注意】Node環境不能使用Web APIs,下面會介紹訪問瀏覽器環境和NW.js的API
-
創建新的Node環境
- 通過
Window.open()
創建新窗口,並且參數new_instance
設置爲true時; - 命令行啓動NW.js時,參數增加
--mixed-context
進入混合環境模式;
- 通過
-
訪問瀏覽器環境和NW.js的API
Node環境中,沒有瀏覽器或NW.js APIS,如
alert()
、document.*
、new.Clipboard
等,想訪問瀏覽器APIs**必須傳遞相應的對象
**,如window
舉個🌰
Node腳本(
test.js
)//node環境中訪問瀏覽器的API需要瀏覽器環境傳入對應的el對象 exports.setText = (el) => { el.innerHTML = 'hello'; };
瀏覽器(
index.html
)<html> <head> ... </head> <body> <div id="el"></div> <script> //加載 test.js Node模塊 var myscript = require('./test.js'); //調用Node模塊setText方法將`el`元素傳入Node函數 myscript.setText(document.getElementbyId('el')); // "hello"將顯示在`el`標籤中 </script> </body> </html>
5.2 混合環境模式
當使用--mixed-context
命令行參數運行NW.js時 , 當瀏覽器環境被創建的同時創建了Node環境 .
-
混合模式中加載腳本
NW.js可以通過兩種方式使用混合環境模式 , 一是使用
--mixed-context
命令行參數 , 二是在配置文件中添加chromium-args
屬性.package.json
{ "name": "test-context", "main": "index.html", "chromium-args": "--mixed-context" //chromium-args屬性指定使用混合模式 }
頁面或Node.js中使用
require()
方式加載腳本運行在同樣的環境中 . -
全局對象
混合環境模式 , 您可以在Node模塊中使用所有瀏覽器和NW.js API,反之亦然
node.js文件(test.js)
//導出showAlert函數 exports.showAlert = function() { //因爲開啓了混合環境摸模式,可以在Node中直接使用瀏覽器的alert API alert("我正在Node模塊中運行!"); };
Index.html
<html> ... <body> <script> //加載 test node模塊 var test = require('./test'); //調用模塊的showAlert方法 myscript.showAlert(); // 我正在Node模塊中運行! </script> </body> </html>
5.3 混合環境模式和獨立環境模式對比
獨立環境模式的優勢是不會出現類型檢查問題 .
混合環境模式的缺點是不能輕易的分享變量 . 環境間分享變量 , 需要將變量放入其他環境能夠訪問的通用環境中 . 或者可以使用window.postMessage()
API在環境之間發送和接收信息 .
6. JavaScript源碼保護
-
源碼保護目的
應用中的javascript源代碼能夠編譯爲本地二進制代碼進行保護,NW.js能夠加載編譯之後的代碼,應用作爲產品發佈時可以將代碼進行編譯!
-
如何編譯和加載
-
編譯
JS源碼編譯爲本地二進制代碼需要使用
nwjc
工具,同時需要提供SDK構建方式的NW.nwjc source.js binary.bin
*.bin
文件需要發佈到應用中,可以任意命名bin文件。 -
加載
NW.js 加載已編譯的JS文件
nw.Window.get().evalNWBin(frame,'binary.bin');
Win.evalNWBin()方法中的參數與
Window.eal()
方法相同,第一個參數爲目標frame(null爲主frame),第二個參數爲已編譯的bin文件 -
加載遠程已編譯的JS文件
NW.js可以從遠程(例如AJAX)獲取已編譯的JavaScropt,並且即時執行。
//創建請求實例HMLHttpRequest var xhr = new HMLHttpRequest(); //設置響應類型-ArrayBuffer對象二進制數據(告訴服務器期望的響應格式) xhr.responseType = 'arraybuffer'; //設置請求方式和地址 xhr.open('GET',url,true); //發起請求 xhr.send(); //接收響應 xhr.onload=()=>{ //接收到響應使用NW加載已編譯的js文件 nw.Window.get().evalNWBin(null,xhr.response); }
**PS:**已編譯代碼在瀏覽器環境中執行 . 可以像其他運行在瀏覽器環境中的其他腳本 , 就像您使用任何Web API(如DOM)以及NW.js API和Node API
-
-
不足
已編譯代碼不支持跨平臺也不兼容不同版本的NW.js的版本,因此在打包應用時需要在各自系統平臺中運行
nwjc
.
7. 應用簽名
應用簽名可阻止正式應用中加載未簽名的文檔。
注意:應用簽名並不能阻止別有用心的人黑進你的應用,或者使用其他NW代碼加載你的應用,可考慮使用C++編寫,並使用Node.js模塊和NaCl加載,或者結合使用nwjc
對源碼進行編譯爲本地二進制文件後使用!
-
簽名文件
NW.js的應用簽名文件爲
verified_contents.json
,它提供了密鑰對,該文件由sign.py
工具與私鑰文件private_key.pem
所創建,公鑰已經內置於NW.js應用中。 -
如何簽名
要運行簽名的應用程序,在應用程序目錄中調用
nw --verify-content=enfore_strict
命令執行嚴格驗證模式,之後顯示簽名頁面,完成簽名動作。在此之後對
index.html
進行任意修改,NW將報告文件已損壞並立即退出。 -
簽名步驟
使用密鑰對簽名應用,步驟如下:
- 切換到應用目錄中
- 確認
verified_contents.json
或computed_hashes.json
文件不在當前目錄(如果存在刪除) - 運行
payload
生成sign.py
所需的配置文件payload.json
文件 - 運行
python sign.py > /tmp/verifid_content.json
,需要注意 tmp目錄不能是應用所在目錄 - 將生成的
verified_contents.json
文件拷貝到應用目錄,完成簽名動作。
-
使用自己密鑰對重新構建應用
要使用您自己的密鑰對,您需要重建NW,並命令行參數
--verify-content=
默認設置爲enfoce_strict
openssl genrsa -out private_key.pem 2048
生成密鑰對,輸出公私鑰文件- 運行
python convertkey.py
,它會將公鑰轉換爲C源代碼 - 將生成的代碼複製到NW安裝包的
content/nw/src/nw_content_verifier_delegate.cc
,替換原文件默認的key值 - 更改文件中第73行爲
Mode experiment_value = ContentVerifierDelegate::ENFORCE_STRICT;
- 重新構建NW
7. 自動更新
詳細使用參見此鏈接
8. Node.js 自定義模塊
Node應用由模塊組成,每個文件就是一個模塊,有自己的作用域
。在一個文件裏面定義的變量、函數、類,都是私有的
,對其他文件不可見。每個模塊內部,module
變量代表當前模塊。這個變量是一個對象
它的exports屬性(即module.exports
)是對外的接口。加載某個模塊
,其實是加載該模塊的module.exports屬性。
-
module.export(
導出模塊
)module.exports
是對外的接口。加載某個模塊
,其實是加載該模塊的module.exports屬性。舉個🌰
/** *創建一個名爲test.js的 Node.js文件 */ var x = 5; var addX = (value)=>{ return value + x; } module.export.x = x; module.export.addX = addX;
上面代碼通過
module.export
導出變量x和函數addX -
require(
加載模塊
)/** *創建名爲 index.js 的 javascript文件 */ //通過require加載模塊 var example = require('./test.js'); //調用模塊中的變量和函數 console.log(example.x); // 5 console.log(example.addX(1)); // 6
-
exports 與 module.exports區別
爲了方便我們可以直接在exports
對象
上添加方法,表示對外輸出的接口,如同在module.exports
上添加的一樣。注意:不能直接將exports變量指向一個值,因爲這樣等於切段了exports與module.exports的聯繫錯誤寫法
export='將exports變量指向該值'
正確寫法(car.js)
-
導出變量
//導出color變量 export.carColor='red'
-
導出函數
//導出setCarColor函數 export.setCarColor=(color)=>{ console.log('color:' + color); }
-
導出對象
/** *導出Car對象包含如下屬性和方法 *@attribute:[brand,color,price] *@method:[setCarColor,] */ export.Car={ brand:'', //品牌 color:'', //顏色 price:20000, //價格 //設置品牌 setBrand:(brand)=>{ this.brand = brand; }, //設置顏色 setCarColor:(color)=>{ this.color = color; }, //設置價格 setPrice:(price)=>{ this.price=price; }, //獲取汽車信息 getCarInfo:()=>{ return '這是一輛'+this.price+this.color+this.brand+'汽車'; }, }
在JS(index.js)中使用Node.js
//加載car.js 模塊 let carNodeJs = require('./car'); _printLog=()=>{ custom.Car.setBrand('蘭博基尼'); custom.Car.setColor('紅色'); custom.Car.setPrice(2000000); alert(custom.Car.getCarInfo()); }
-
9. ES6與NodeJS模塊導入導出區別
NodeJs
-
在Node模塊中,採用的是commonjs規範,也就是使用**
require
方式引入模塊,而使用module.exports
**導出接口 -
如何檢測Node.js對ES6的支持
- 命令行全局安裝
es-checker
- 執行 es-checker
- 紅色表示暫不支持的
- 命令行全局安裝
ES6
- Es6模塊中並沒有採用node中require導入模塊的方式,導入(import),導出(export)