uniapp使用命令行打包app,用脚本修改打包配置,让打包更方便

3,978 阅读3分钟

需求

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

实现效果

思路:

  1. 编写脚本更改配置文件,以及替换应用图标
  2. 采用inquirer库,实现命令行的交互效果
  3. 使用fs-extra修改文件以及复制文件等操作
  4. 利用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      # 命令行交互效果,详细可查阅inquirer库
npm install fs-extra -D      # 用于修改文件
npm install iconv-lite -D    # 解决子进程输出到命令行时 乱码问题

步骤三:编写脚本build.js

  • 注意:需要将hbuilder文件中的cli.exe所在的文件夹添加到环境变量中,否则执行时会找不到cli.exe
/*
 * 脚本执行顺序:
 * 1. 通过inquirer,获取用户的选择
 * 2. 更改配置文件内容:包括:manifest配置,build.json打包配置文件,隐私文件配置文件等。
 * 3. 执行命令行命令:cli.exe pack --config build.json
 */
const inquirer = require('inquirer')
const fs = require('fs-extra')
const path = require('path')
const { exec } = require('child_process')
const iconv = require('iconv-lite')

// 命令行交互,返回值是promise對象
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)
        // 获取配置文件中的应用名,应用ID,以及manifest的所有数据,readManifest方法定义在下方
	let { appName, data, appId } = readManifest()
	// 如果选择的appname与目前配置文件的appname不同
	if (appName !== res.appName) {
		// 修改app相关配置文件,replaceManifest方法定义在下方
		replaceManifest(appName, res.appName, appId, data)
		// 修改隐私政策文件,replacePrivacy方法定义在下方
		replacePrivacy(appName, res.appName)

	} else {
		console.log('\n应用名不变,无需修改配置文件')
	}
	// 修改配置环境env.js,replaceEnv方法定义在下方
	replaceEnv(res.env)
	// 修改打包配置文件build.json,replaceBuild方法定义在下方
	replaceBuild(res.appName, res.env, res.platform)
	console.log('\n开始打包')
        // 获取打包配置的路径
	const buildPath = path.resolve(__dirname, '../build.json')
	// 执行打包命令,commandSpawn方法定义在下方
	commandSpawn(`cli.exe pack --config ${buildPath}`)
})

// 执行命令行命令
const commandSpawn = function (arg) {
	return new Promise((resolve, reject) => {
		// exec执行后会返回一个子进程,encoding: 'binary'配合iconv解决乱码问题
		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()
		})
	})
}

// 修改build.json文件
const replaceBuild = function (appname, env, platform) {
        // 读取build.json文件的数据
	const data = JSON.parse(fs.readFileSync('build.json', 'utf-8'))
	// 设置打包平台
	data.platform = platform
	// 设置打包id
	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')
	// 修改ios证书
	data.ios.certfile = path.resolve(__dirname, `../common/certificate/ios/${appname}/p12.p12`)
	if (env === 'development') {
		// 开发环境,Android不选择渠道
		data.android.channels = ''
                // ios描述文件,开发环境和生成环境下不一致
		data.ios.profile = path.resolve(__dirname,
			`../common/certificate/ios/${appname}/adhoc.mobileprovision`)
	} else {
		// 生产环境,选择渠道是google,具体配置参考https://hx.dcloud.net.cn/cli/pack
		data.android.channels = 'google'
                // IOS描述文件
		data.ios.profile = path.resolve(__dirname,
			`../common/certificate/ios/${appname}/adstore.mobileprovision`)
	}
        // 修改打包配置
	fs.writeFileSync('build.json', JSON.stringify(data))
	console.log('修改build.json文件成功')
}


// 读取manifest文件
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
	}
}

// 修改manifest文件
const replaceManifest = function (oldName, newName, oldId, data) {
	const appid = {
		APP1_NAME: '__UNI__197',
		APP2_NAME: '__UNI__8AB'
	}
	// 修改名字
	data.name = newName
	// 修改id
	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`
	}
        // ios自定义启动页
	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')
	}
}