上期讲到用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

项目地址:

Github


蔚蓝
382 声望54 粉丝

微前端实践者,开源项目作者。


引用和评论

0 条评论