web

NW.js

另一款全端打包应用

Posted by Lorry on February 10, 2019

文章字数:9778, 阅读全文大约需要:27 分钟

之前写过一个使用 electron 进行打包的文章, 但是现在公司又有需求是需要在 xp 上适配, 而 electron 是不支持 xp 的, 于是就想到了另一款打包工具, nwjs.

nwjs全称 Node-Webkit, 可以同时使用 DOM 和 node.js 的所以模块.所以就赋予了桌面端的功能.

这里使用 nwjs-builder-phoenix, 可以支持命令行打包.环境选择 windows, mac 类似, 但 mac 我只打出了 .app 包, 没有 developer 账号, 没法进行签名.所以体验不如 windows 好.

1.npm init 生成 package.json, yarn add nwjs-builder-phoenix -D 安装库

2.添加待打包文件到 build 文件夹下:index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>NWJS-demo</title>
</head>
<body>
  <h1>This is one demo for application built by nwjs</h1>
  <button onclick="nw.Window.open('http://www.baidu.com')">open default</button>
  <button onclick="nw.Window.open('http://www.baidu.com', {
    title: 'I Love China', // 不会生效的, 只对应用的第一个窗口有效
    width: 100,
    height: 100,
    position: 'mouse',
    focus: true
  }, (new_window) => console.log('after new window created:', new_window))">open customer</button>
  <button onclick="nw.Window.open('http://docs.nwjs.io/en/latest/References/Window/#windowopenurl-options-callback')">see window detail</button>
</body>
</html>

3.添加配置生成文件, application_generator.js

var shell = require('shelljs');
var fs = require('fs');
var readline = require('readline');

var length = process.argv.length;
// 获取应用平台类型
var platform = process.argv[length - 1];

// 获取版本号
var version_json = JSON.parse(fs.readFileSync('./versions.json', 'utf8'));
if (platform == 'win') {
  var new_package_json = JSON.parse(fs.readFileSync('./application/win_package.json', 'utf8'));
  new_package_json.version = version_json.current;
  shell.rm('-rf', 'application_win');
  shell.mkdir('application_win');
  shell.cp('-R', 'build/', 'application_win/');
  shell.cp('-R', 'nsis/', 'application_win/');
  generate_nsis_file('application_win/nsis/kitten.nsi');
  fs.writeFileSync('application_win/package.json', JSON.stringify(new_package_json, null, 2));
} else if (platform == 'xp') {
  var new_package_json = JSON.parse(fs.readFileSync('./application/xp_package.json', 'utf8'));
  new_package_json.version = version_json.current;
  shell.rm('-rf', 'application_xp');
  shell.mkdir('application_xp');
  shell.cp('-R', 'build/', 'application_xp/');
  shell.cp('-R', 'nsis/', 'application_xp/');
  generate_nsis_file('application_xp/nsis/kitten.nsi');
  fs.writeFileSync('application_xp/package.json', JSON.stringify(new_package_json, null, 2));
}
// 动态更新 version
function generate_nsis_file(path) {
  var fread = fs.createReadStream('./nsis/kitten.nsi');
  var version_line = 6;
  var obj_readline = readline.createInterface({
    input: fread,
  });
  var count = 1;
  var str = '';
  obj_readline.on('line', (line) => {
    if (version_line == count) {
      str = `${str}!define DEMO_VERSION ${version_json.current}\r\n`;
    } else {
      str += line + '\r\n';
    }
    count ++;
  });
  obj_readline.on('close', () => {
    fs.writeFileSync(path, str);
  });
}

4.添加打包用的 win_package.json, mac_package.json, xp_package.json 对应不同的平台, 一下配置基本上涵盖了所有的 build 参数也都写了注释.这些参数也可以在打开新窗口 nw.Window.open(‘url’, {options})中使用

{
  "name": "nwjs-demo",
  "main": "build/index.html",
  "build": {
    "nwVersion": "0.32.0",
    "packed": false,
    "nwFlavor": "sdk", // 是否允许打开 devtools, normal 表示最小化 build , 不包含 devtool 和 NaCI
    "mute": false,
    "ffmpegIntegration": true, // 是否集成 iteufel/nwjs-ffmpeg-prebuilt 以播放 .mp3等媒体资源, 如果设置为 true, nwVersion 不能使用符号
    // 哪些文件需要打包进去, 排除 output 的文件夹
    "files": [
      "build/**",
      "package.json"
    ],
    // 以下内容会显示在文件的属性中
    "win": {
      "productName": "nwjs-demo",
      "companyName": "lorry ltd",
      "fileDescription": "This is a demo",
      "productVersion": "1.0.0",
      "fileVersion": "1.0.0",
      "icon": "" // 放可执行文件的图标路径
    },
    "nsis": {
      "icon": "这里是打包成 nsis 安装包后的 icon 路径",
      "diffUpdaters": false
    }
  },
  "user-agent": "用于覆盖应用 HTTP 请求的 User-Agent 请求头, %name 表示 manifest 的 name, %ver 表示 manifest 的 version,%nwver 表示 NW.js 的版本号 %webkit_ver 表示 Blink 引擎的版本, %osinfo 表示系统的 OS 和 CPU 信息",
  "window": {
    "title": "nwjs-demo", // 这是桌面应用上的标题
    "width": 1200,
    "height": 768,
    "min-width": 900,
    "min-height": 500,
    "max-width": 900,
    "max-height": 500,
    "position": "center", // 可选 null, center, mouse
    "resizable": true,
    "icon": "", // 这是应用程序的左上角 icon和任务栏地址必须用128*128的 png 格式
    "always_on_top": false,
    "fullscreen": false,
    "show_in_taskbar": true, // 默认为 true, 在任务栏显示 icon
    "kiosk": false, // 特殊模式, 强制全屏且无法退出, 主要用于展示时使用
    "transparent": false, //默认为 false
    "frame": false, // 使整个界面没有框, 注意一定不能再设置为全屏, 那样就无法在边缘捕获到鼠标
    "visible_on_all_workspaces": false, // mac和 linux 下可用, 表示界面是否对所有的workspace可见
    "id": "abc" // 如果再次打开相同 id 的 window, 会恢复窗口上次打开的大小和位置.
  },
  "domain": "", // 这是chrome-extension://xxxxx 的 xxxxx, 表示应用的主机名, 可以与网站共享同一个域的 cookies
  // 表示chromium的命令, 可以设置跨域, cookies,等等
  "chromium-args": "--enable-file-cookies --allow-running-insecure-content --ignore-certificate-errors --enable-logging --log-level=0"
}

5.添加打包成 installer的 nsis 文件, 记住一定要将格式转换为 utf8 with DOM, 不然里面写的中文会报字符编码的错误

; Unicode is not enabled by default.
Unicode true
!include "MUI2.nsh"

; The version value is updated in script
!define DEMO_VERSION 1.0.0

;;;General
Name "nwjsDemo"
OutFile "..\dist\nwjsDemo_${DEMO_VERSION}_安装包.exe"
; Icons
!define MUI_ICON "demo.ico"

;Default installation folder
InstallDir "$LOCALAPPDATA\nwjs-demo"

;Request application privileges for Windows Vista
RequestExecutionLevel user

;;;Interface Settings
!define MUI_ABORTWARNING

;;;Pages
!insertmacro MUI_PAGE_INSTFILES

!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES

!insertmacro MUI_LANGUAGE "SimpChinese"

;;; Languages
;LoadLanguageFile "${NSISDIR}\Contrib\Language files\SimpChinese.nlf"
VIProductVersion "${DEMO_VERSION}.0"
 VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "ProductName" "nwjs-demo"
 VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "Comments" "nwjs-demo"
 VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "CompanyName" "lorry ltd"
 VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "LegalCopyright" "Copyright (c) 2015-2019 Lorry"
 VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "FileDescription" "nwjs-demo客户端"
 VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "FileVersion" "${DEMO_VERSION}"
 VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "ProductVersion" "${DEMO_VERSION}.0";版本号

;;;Installer Section
Section "Install Section" InstallSection
  SetOutPath "$INSTDIR"

  ;Put files
  ;自有格式
  File jlr.ico
  File demo.ico
  File demo_tab.png
  ;File /r demo/*
  File /r ..\dist\nwjs-demo-${DEMO_VERSION}-win-x86\*

  ;Create uninstaller
  WriteUninstaller "$INSTDIR\uninst.exe"

  ;;Write registry
  ;Save installation path
  WriteRegStr HKCU SOFTWARE\demo "InstallDir" "$INSTDIR"
  ;定义卸载路径变量
  !define UNINST "Software\Microsoft\Windows\CurrentVersion\Uninstall\demo"

  ;Write uninstall keys, 这里是显示在控制面板的卸载程序里.
  WriteRegStr HKCU UNINST "DisplayName" "nwjs-demo" ;应用名
  WriteRegStr HKCU UNINST "UninstallString" "$INSTDIR\uninst.exe";卸载程序位置
  WriteRegStr HKCU UNINST "ProductVersion" "${DEMO_VERSION}.0";版本号
  WriteRegStr HKCU UNINST "DisplayIcon" "$INSTDIR\ jlr.ico";icon
  WriteRegStr HKCU UNINST "Publisher" " 蒋礼锐";开发者

  ;Associate jlr file format 自有格式
  ;First delete any existing file extension bindings.
  DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.jlr";
  WriteRegStr HKCU "Software\Classes\.jlr" "" "JLRFile"
  WriteRegStr HKCU "Software\Classes\JLRFile\shell\open\command" "" "$\"$INSTDIR\nwjs-demo.exe$\" $\"%1$\""
  WriteRegStr HKCU "Software\Classes\JLRFile\DefaultIcon" "" "$INSTDIR\jlr.ico,0"; 显示 icon

SectionEnd

Section "Start Menu Shortcuts"
  CreateDirectory "$SMPROGRAMS\nwjs-demo"
  CreateShortcut "$SMPROGRAMS\nwjs-demo\nwjs-demo.lnk" "$INSTDIR\nwjs-demo.exe" "" "$INSTDIR\nwjs-demo.exe" 0
  CreateShortcut "$SMPROGRAMS\nwjs-demo\uninst.lnk" "$INSTDIR\uninst.exe" "" "$INSTDIR\uninst.exe" 0

  ; Also create desktop shortcut.
  CreateShortCut "$desktop\nwjs-demo.lnk" "$INSTDIR\nwjs-demo.exe" "" "$INSTDIR\nwjs-demo.exe" 0
SectionEnd

Section "Finish Installation" FinishInstall
  ExecShell "" "$INSTDIR\nwjs-demo.exe";完成之后自动打开应用
  SetAutoClose true;是否自动关闭
SectionEnd

;;;Uninstaller Section
Section "Uninstall"
  ;Delete program folder
  RMDIR /r "$INSTDIR"

  ;Delete shortcut in startup menu
  Delete "$SMPROGRAMS\nwjs-demo\nwjs-demo.lnk";要全部删除应用程序和卸载程序的快捷方式才能移除掉文件夹
  Delete "$SMPROGRAMS\nwjs-demo\uninst.lnk"
  RMDIR "$SMPROGRAMS\nwjs-demo";移除文件夹
  Delete "$desktop\nwjs-demo.lnk";移除桌面快捷方式

  ;Cleanup registry
  DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\demo" "DisplayName"
  DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\demo" "UninstallString"
  DeleteRegValue HKCU SOFTWARE\demo "InstallDir"

  ;Unregister file association
  ;${unregisterExtension} ".jlr" "jlr Format"
  DeleteRegKey HKCU "Software\Classes\.jlr"
  DeleteRegKey HKCU "Software\Classes\JLRFile\shjell\open\command"

SectionEnd

6.添加打包命令, package.json

{
  "name": "nw",
  "version": "1.0.0",
  "description": "",
  "main": "build/index.html",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "application:generator:win7": "node application_generator.js win",
    "dist:nw:win7": "build --tasks win-x86 --mirror https://npm.taobao.org/mirrors/nwjs/ ./application_win",
    "dist:nsis:win7": "cd application_win/nsis && ../../node_modules/nwjs-builder-phoenix/assets/nsis/makensis.exe demo.nsi"
  },
  "author": "Lorry",
  "license": "ISC",
  "devDependencies": {
    "fs": "^0.0.1-security",
    "nwjs-builder-phoenix": "^1.15.0",
    "shelljs": "^0.8.3"
  }
}

7.然后依次执行 application:generator:win7, dist:nw:win7, dist:nsis:win7即可打包出一个安装包

然后需要注意的点是如果需要打包成 xp 的话, 请使用nwVersion: 0.14.7的版本即可, 就是换了个壳, 如果网页版在 xp 下打不开那nwjs 也无能为力.

所有文件及项目结构均在我的仓库中, 可 clone 下来把你的 build 文件拷入即可用