上期讲到用nw.js打包vuecli3创建的项目为桌面程序:传送门:使用nw.js将vue项目打包为可在xp系统运行的桌面程序。打包完成之后却发现打包出来的是一个文件夹,里面有可执行程序exe和一堆环境或资源文件,这么一个文件夹丢给客户安装显然太不优雅。
这期就讲下如何将这一堆东西美化成一个.exe安装包。本文先粗浅研究美化程序在vuecli3项目上的实现,主要参考不爱吃西红柿的文章:用 vue2 和 webpack 快速建构 NW.js 项目(3),并将其修改为在vuecli3项目上的实现。
美化nwjs安装包
上个文章已经完成了vuecli3项目打包成.exe,现在接着处理美化程序。
先安装相关依赖
npm install iconv-lite innosetup-compiler --save-dev
创建config/setup.iss文件
复制即可,后面理解了可以自行修改;另外如果安装包出现乱码,可将setup.iss文件修改后缀为txt然后以ansi格式保存后再改回setup.iss
; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
; This CWD is the directory where the `setup.iss`, pay attention to join the relative directory!
; 该执行目录为 `setup.iss` 所在的目录,请注意拼接相对目录
#define MyAppName "_name_"
#define MyAppAliasName "_appName_"
#define MyAppVersion "_version_"
#define MyAppPublisher "_appPublisher_"
#define MyAppURL "_appURL_"
#define MyAppExeName "_name_.exe"
#define OutputPath "_outputPath_"
#define OutputFileName "_outputFileName_"
#define SourceMain "_filesPath_\_name_.exe"
#define SourceFolder "_filesPath_\*"
#define LicenseFilePath "_resourcesPath_\license.txt"
#define SetupIconFilePath "_resourcesPath_\logo.ico"
#define MyAppId "_appId_"
[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={#MyAppId}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
AppVerName={#MyAppAliasName}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={pf}\{#MyAppName}
LicenseFile={#LicenseFilePath}
OutputDir={#OutputPath}
OutputBaseFilename={#OutputFileName}
SetupIconFile={#SetupIconFilePath}
Compression=lzma
SolidCompression=yes
PrivilegesRequired=admin
Uninstallable=yes
UninstallDisplayName={#MyAppAliasName}
DefaultGroupName={#MyAppAliasName}
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: checkedonce
[Files]
Source: {#SourceMain}; DestDir: "{app}"; Flags: ignoreversion
Source: {#SourceFolder}; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
[Messages]
SetupAppTitle={#MyAppAliasName} setup wizard
SetupWindowTitle={#MyAppAliasName} setup wizard
[Icons]
Name: "{commondesktop}\{#MyAppAliasName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
Name: "{group}\{#MyAppAliasName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{group}\uninstall {#MyAppAliasName}"; Filename: "{uninstallexe}"
[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
新增build/setup.js
var innosetupCompiler = require('innosetup-compiler')
var path = require('path')
var fs = require('fs')
var iconv = require('iconv-lite')
function resolve() {
return path.resolve.apply(path, [__dirname, '..'].concat(...arguments))
}
var rootPath = path.resolve(__dirname, '../')
// `./package.json`
var tmpJson = require(path.resolve(rootPath, './package.json'))
var curReleasesPath = resolve('./releases');
// 约定性配置
var setupOptions = {
issPath: resolve('./config/setup.iss'),
// only one version path
files: curReleasesPath,
resourcesPath: resolve('./build/setup_resources'),
appPublisher: 'nw-vue-demo, Inc.',
appURL: 'your url',
appId: '{{your id}}',
// data: { name, version, platform }
};
fs.readdir(setupOptions.files, function (err, files) {
if (err) throw err
files.forEach(function (fileName) {
if (!~fileName.indexOf('win')) return
const curPath = path.resolve(setupOptions.files, fileName)
fs.stat(curPath, function (err, stats) {
if (err || stats.isFile()) return
if (stats.isDirectory()) {
makeExeSetup(Object.assign({}, setupOptions, { files: curPath, platform: fileName }))
}
})
})
})
function makeExeSetup(opt) {
const { issPath, files, resourcesPath, appPublisher, appURL, appId, platform } = opt
const { name, version } = tmpJson
const tmpIssPath = path.resolve(path.parse(issPath).dir, '_tmp.iss')
return new Promise(function (resolve, reject) {
// rewrite name, version to iss
fs.readFile(issPath, null, function (err, text) {
if (err) return reject(err)
// 这里是iss文件里的变量配置的地方
let str = iconv.decode(text, 'gbk')
.replace(/_name_/g, name) // 名字
.replace(/_appName_/g, name) // app名字,这里用name代替
.replace(/_version_/g, version) // 版本
.replace(/_outputPath_/g, 'app') // 美化程序输出的路径,这里写死config/app,则美化程序打包后将在config/app/下生成
.replace(/_outputFileName_/g, name + version) // 输出的文件名
.replace(/_filesPath_/g, files) // 要打包的文件路径
.replace(/_resourcesPath_/g, resourcesPath) // 资源路径,主要是图标、安装协议等,复制build下的setup_resources目录即可
.replace(/_appPublisher_/g, appPublisher) // 软件出版商,自己写
.replace(/_appURL_/g, appURL) // app线上路径
.replace(/_appId_/g, appId) // appid
fs.writeFile(tmpIssPath, iconv.encode(str, 'gbk'), null, function (err) {
if (err) return reject(err)
// inno setup start
innosetupCompiler(tmpIssPath, { gui: false, verbose: true }, function (err) {
fs.unlinkSync(tmpIssPath)
if (err) return reject(err)
resolve(opt)
})
})
})
})
}
主要的调整修改就是在setup.js文件
修改完成,先打包nw文件,再打包美化文件
yarn build:nw
yarn setup
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。