需求
- 同一套代码,生成两个应用app
- 这需要在打包的时候更改manifest配置文件的应用名称,应用图标,应用ID
- 每次手动更改配置文件再进行打包,十分麻烦,通过脚本修改配置文件后,再执行命令行命令进行打包
实现效果

思路:
- 编写脚本更改配置文件,以及替换应用图标
- 采用inquirer库,实现命令行的交互效果
- 使用fs-extra修改文件以及复制文件等操作
- 利用node的child_process,在脚本中执行打包命令,详细可参考:hx.dcloud.net.cn/cli/pack
实现步骤
步骤一:修改package.json文件
- 在命令行执行:npm run build 就等同于执行build.js
"scripts": {
"build": "node ./utils/build.js"
},
步骤二:下载相应的依赖
npm install inquirer -D
npm install fs-extra -D
npm install iconv-lite -D
步骤三:编写脚本build.js
- 注意:需要将hbuilder文件中的cli.exe所在的文件夹添加到环境变量中,否则执行时会找不到cli.exe
const inquirer = require('inquirer')
const fs = require('fs-extra')
const path = require('path')
const { exec } = require('child_process')
const iconv = require('iconv-lite')
inquirer.prompt([
{
type: 'list',
message: '请选择打包应用',
name: 'appName',
choices: ['APP1_NAME', 'APP2_NAME']
},
{
type: 'list',
message: '请选择环境',
name: 'env',
choices: ['production', 'development']
},
{
type: 'list',
message: '请选择平台',
name: 'platform',
choices: ['android', 'ios', 'android,ios']
}
]).then(res => {
console.log("\n您选择的是:", res)
let { appName, data, appId } = readManifest()
if (appName !== res.appName) {
replaceManifest(appName, res.appName, appId, data)
replacePrivacy(appName, res.appName)
} else {
console.log('\n应用名不变,无需修改配置文件')
}
replaceEnv(res.env)
replaceBuild(res.appName, res.env, res.platform)
console.log('\n开始打包')
const buildPath = path.resolve(__dirname, '../build.json')
commandSpawn(`cli.exe pack --config ${buildPath}`)
})
const commandSpawn = function (arg) {
return new Promise((resolve, reject) => {
const childProcess = exec(arg, {
encoding: 'binary'
})
childProcess.stdout.on('data', data => {
console.log(iconv.decode(Buffer.from(data, 'binary'), 'gbk'))
})
childProcess.stderr.on('data', data => {
console.log(iconv.decode(Buffer.from(data, 'binary'), 'gbk'))
})
childProcess.on('exit', () => {
resolve()
})
})
}
const replaceBuild = function (appname, env, platform) {
const data = JSON.parse(fs.readFileSync('build.json', 'utf-8'))
data.platform = platform
const name = {
APP1_NAME: 'uni.UNI197',
APP2_NAME: 'uni.UNI8AB'
}
data.ios.bundle = name[appname]
data.android.packagename = name[appname]
data.android.certfile = path.resolve(__dirname, '../common/certificate/android.keystore')
data.ios.certfile = path.resolve(__dirname, `../common/certificate/ios/${appname}/p12.p12`)
if (env === 'development') {
data.android.channels = ''
data.ios.profile = path.resolve(__dirname,
`../common/certificate/ios/${appname}/adhoc.mobileprovision`)
} else {
data.android.channels = 'google'
data.ios.profile = path.resolve(__dirname,
`../common/certificate/ios/${appname}/adstore.mobileprovision`)
}
fs.writeFileSync('build.json', JSON.stringify(data))
console.log('修改build.json文件成功')
}
const readManifest = function () {
const data = JSON.parse(fs.readFileSync('manifest.json', 'utf-8'))
const appName = data.name
const appId = data.appid
return {
appName,
appId,
data
}
}
const replaceManifest = function (oldName, newName, oldId, data) {
const appid = {
APP1_NAME: '__UNI__197',
APP2_NAME: '__UNI__8AB'
}
data.name = newName
data.appid = appid[newName]
data['app-plus'].distribute.splashscreen.android = {
"hdpi": `common/startPage/android/${newName}_hdpi.9.png`,
"xhdpi": `common/startPage/android/${newName}_xhdpi.9.png`,
"xxhdpi": `common/startPage/android/${newName}_xxhdpi.9.png`
}
data['app-plus'].distribute.splashscreen.ios.storyboard = `common/startPage/ios/${newName}.zip`
data['app-plus'].distribute.icons = {
"android": {
"hdpi": `common/logo/${newName}/icons/72x72.png`,
"xhdpi": `common/logo/${newName}/icons/96x96.png`,
"xxhdpi": `common/logo/${newName}/icons/144x144.png`,
"xxxhdpi": `common/logo/${newName}/icons/192x192.png`
},
"ios": {
"appstore": `common/logo/${newName}/icons/1024x1024.png`,
"ipad": {
"app": `common/logo/${newName}/icons/76x76.png`,
"app@2x": `common/logo/${newName}/icons/152x152.png`,
"notification": `common/logo/${newName}/icons/20x20.png`,
"notification@2x": `common/logo/${newName}/icons/40x40.png`,
"proapp@2x": `common/logo/${newName}/icons/167x167.png`,
"settings": `common/logo/${newName}/icons/29x29.png`,
"settings@2x": `common/logo/${newName}/icons/58x58.png`,
"spotlight": `common/logo/${newName}/icons/40x40.png`,
"spotlight@2x": `common/logo/${newName}/icons/80x80.png`
},
"iphone": {
"app@2x": `common/logo/${newName}/icons/120x120.png`,
"app@3x": `common/logo/${newName}/icons/180x180.png`,
"notification@2x": `common/logo/${newName}/icons/40x40.png`,
"notification@3x": `common/logo/${newName}/icons/60x60.png`,
"settings@2x": `common/logo/${newName}/icons/58x58.png`,
"settings@3x": `common/logo/${newName}/icons/87x87.png`,
"spotlight@2x": `common/logo/${newName}/icons/80x80.png`,
"spotlight@3x": `common/logo/${newName}/icons/120x120.png`
}
}
}
fs.writeFileSync('manifest.json', JSON.stringify(data))
console.log('\n修改manifest.json文件成功')
}
const replacePrivacy = function (oldName, newName) {
let data = fs.readFileSync('androidPrivacy.json', 'utf-8')
data = data.replace(new RegExp(name[oldName], 'g'), newName)
fs.writeFileSync('androidPrivacy.json', data)
console.log('修改androidPrivacy.json文件成功')
}
const replaceEnv = function (newEnv) {
let data = fs.readFileSync('env.js', 'utf-8')
let oldEnv = data.match(/let environment = '(.*?)'/)[1]
if (oldEnv !== 'newEnv') {
data = data.replace(oldEnv, newEnv)
fs.writeFileSync('env.js', data)
console.log('修改env.js文件成功')
} else {
console.log('所选与原有环境相同,不修改env.js')
}
}