1.背景
是一个类似微前端的应用,但是没有qiankun那么高级的,通过routeId区分打包不同的微应用,嵌入app,手动打包的时候只可以打包一个应用 会有一种情况:app第一次上线所有微应用、改了公共的文件需要应用到所有微应用中,就需要打包多个或者全部模块 写个简单脚本先...
2.基础文件
webpack.prod.js
const path = require('path')
const { merge } = require('webpack-merge')
// webpack基础配置,entry、output、optimization、resolve、module等
const baseWebpackConfig = require('./webpack.base')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const ProgressBarPlugin = require('progress-bar-webpack-plugin')
const webpack = require('webpack')
// 路由的配置文件 const config = require('../src/config.js')
const router = `./main/${config.routerId}.js`
// 打包后的文件写到根目录下的www文件
const copyPluginConfig = [
{
from: path.resolve(__dirname, '../src/sql'),
to: path.resolve(__dirname, '../www/sql'),
},
{
from: path.resolve(__dirname, '../src/lib'),
to: path.resolve(__dirname, '../www/lib'),
},
{
from: path.resolve(__dirname, '../src/variable.js'),
to: path.resolve(__dirname, '../www/variable.js'),
},
]
const workerConfig =
{
from: path.resolve(__dirname, '../worker/worker.js'),
to: path.resolve(__dirname, '../www/worker/index.js'),
}
const setCopyPluginConfig = function() {
config.workerList.indexOf(config.routerId) > -1 ?
copyPluginConfig.push(workerConfig) : null
}
setCopyPluginConfig()
module.exports = merge(baseWebpackConfig,{
mode: 'production',
plugins: [ new CleanWebpackPlugin(),
new webpack.DefinePlugin({
PROCESS_ENV_ROUTER: JSON.stringify(router),
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../src/static/index.template'),
inject: 'body',
}),
new CopyWebpackPlugin(copyPluginConfig),
new ProgressBarPlugin(), ],
})
config.js
// 路由ID字典
const routerIdDict = {
// 离线模块
name1: 'equipment-maintenance',
...
}
module.exports = {
serverHostName: null,
serverPort: 8006,
baseAlias: '',
domain: '',
// appcloud打包环境变量。test为测试环境,不填为正式环境
webVersion: '02',
//web版本,每次热更新需更新此版本号,每次app更新需置零
routerId: routerIdDict.name1,
hotUpdateEnv: '', // 热更新环境,默认空为生产环境,test 测试 beta 预发布
skipAppUpdateUsers: ['mysoft_appstore', 'mysoft_android'], //不更新版本的用户,用于审核
updateFrequency: 1800, //更新请求频率,单位秒
updateShortFrequency: 60, //非稳定版请求频率,单位秒
downloadFrequency: 10,
downPageSize: 200,
clearDataFrequency: 3600,
workerList: ['equipment-maintenance', 'equipment-file', 'integrated-patrol', 'decorate-inspection'], // 离线应用模块:需要打包 worker 代码
routerIdDict: routerIdDict,
}
3.单个模块打包脚本
是通过webpack打包,压缩后上传到oss,拿到oss地址,通过后端脚本更新到对应的应用
3.1 webpack打包
"script":{ "build":"webpack --config webpack/webpack.prod.js" }
3.2 热更新脚本
hotupdate.js
const {exec} = require('child_process');
const path = require('path');
const fs = require('fs');
const config = require('./src/config');
let argv = process.argv.splice(2);
// 基础配置写入打包好的www文件
saveVariable = function () {
let apiDomain = config.domain === 'test' ? 'https://apppb-ywy-test.myysq.com.cn' : 'https://apppb.myysq.com.cn'
let appEnv = config.domain === 'test' ? 'test' : 'dist'
let appVersion = config.webVersion
let appId = config.routerId
let content = `var apiDomain = '${apiDomain}';var APP_ENV = '${appEnv}';
var APP_VERSION = '${appVersion}';
var APP_ID = '${appId}';`
let filePath = path.resolve(__dirname, 'www/variable.js')
try {
fs.writeFileSync(filePath, content);
console.log('variable.js ----------' + content); }
catch (e) {
throw e;
}
};
// 执行hotupdate.sh脚本压缩
zip = function () {
return new Promise((resolve, reject)=>{
exec('sh hotupdate.sh', (error, stdout, stderr) => {
if (error) {
console.error(`执行的错误: ${error}`);
reject();
}
console.log(`stdout: ${stdout}`
);
console.error(`stderr: ${stderr}`);
resolve();
});
});
};
// 上传到oss
ossUpload = function () {
return new Promise((resolve, reject) => {
const fileName = argv[0] || new Date().getTime();
exec('node oss-upload.js ' + fileName, (error, stdout, stderr) => {
if (error) {
console.error(`执行的错误: ${error}`);
reject();
}
console.log(`stdout: ${stdout}`
);
console.error(`stderr: ${stderr}`);
resolve();
});
})
};
fn = async function () {
await saveVariable();
await zip();
await ossUpload();
}();
hotupdate.sh
echo 1 > hotUpdate.zip
rm hotUpdate.zip
cd www zip -q -r ../hotUpdate.zip .
/* echo '构建新包:hotUpdate.zip 完成'*/
oss-upload.js
let OssUpload = require('ali-oss')
let client = new OssUpload({
region: 'oss-cn-hangzhou',
accessKeyId: '',
accessKeySecret: '', bucket: '',
});
// 这里可以拿到上传oss后的地址
async function put() {
try {
let objectName = process.argv.splice(2)[0] || (new Date()).valueOf();
console.log('文件名', objectName);
//object-name可以自定义为文件名(例如file.txt)或目录(例如abc/test/file.txt)的形式,实现将文件上传至当前Bucket或Bucket下的指定目录。
console.log('开始上传')
let result = await client.put('/property-app/hotupdate/' + objectName + '.zip', 'hotUpdate.zip');
// console.log(result);
console.log('结束上传,OSS地址:', result.url.replace('http', 'https'));
} catch (e) {
console.log(e);
}
}
put();
3.3 sql脚本
set names utf8;
INSERT INTO `pb_app_web_version` (`id`, `app_code`, `version_code`, `app_version_code_ios`, `app_version_code_android`, `download_url`, `update_info`, `update_info_url`, `release_date`, `created_on`, `created_by`, `modified_on`, `modified_by`, `is_deleted`)
VALUES (UUID(), 'app代码', '50', '1.2.2', '1.2.1', 'oss地址', NOW(),'', NOW(), NOW(), 'system', NOW(), 'system', '0');
4.模块批量打包
原理很简单:读取config.js中的模块列表,然后循环自动打包,日志输出ossLog.txt
const {exec} = require('child_process')
const fs = require('fs')
const os = require('os')
const config = require('./src/config')
const routerIdList = Object.keys(config.routerIdDict)
const getOsInfo = function () {
let isWin = os.platform() === 'win32' let br = isWin ? '\r\n' : '\n'
return { isWin, br, }
}
const osInfo = getOsInfo()
const setConfig = function (routerId) {
const br = osInfo.br
const data = fs.readFileSync('src/config.js', 'utf8').split(br)
data.forEach((e, i) => {
let routerLine = e.includes("routerId:")
if (routerLine) {
data[i] = `routerId: routerIdDict.${routerId},`
}
})
fs.writeFileSync('src/config.js', data.join(br), 'utf8') }
const build = function (routerId) {
return new Promise((resolve, reject)=>{
exec('yarn run build', (error, stdout, stderr) => {
if (error) {
console.error(`build 执行的错误: ${error}`);
reject(false);
}
console.log(`build stdout: ${stdout}`);
console.error(`build stderr: ${stderr}`);
resolve(true);
});
});
}
const hotupdate = function (routerId) {
return new Promise((resolve, reject)=>{
exec('yarn run hotupdate', (error, stdout, stderr) => {
if (error) {
console.error(`hotupdate 执行的错误: ${error}`);
reject(false);
}
console.log(`hotupdate stdout: ${stdout}`);
let data = filter(routerId, stdout)
fs.appendFileSync('ossLog.txt', data, 'utf8')
console.error(`hotupdate stderr: ${stderr}`
);
resolve(true);
});
});
}
const filter = function (routerId, stdout) {
let list = stdout.split(osInfo.br)
let info = { routerId, env: config.domain ? '测试环境' : '生产环境', version: config.webVersion }
list.forEach((e, i) => {
let variable = e.includes("variable.js")
let ossUrl = e.includes('OSS')
if (variable) { info.variable = e }
else if (ossUrl) { info.ossUrl = e } })
return ` ${info.routerId}: ${info.env} 版本号:${info.version} ${info.ossUrl} `
}
const autoBatchBuild = async function () {
for (let i = 0; i < routerIdList.length; i++) {
const e = routerIdList[i]
setConfig(e)
console.log(`正在构建 ${e} 模块:===================`)
let buildApp = await build(e)
let oss = await hotupdate(e)
if (!buildApp || !oss) { break }
}
}
const main = function () {
fs.writeFileSync('ossLog.txt', '', 'utf8')
autoBatchBuild()
}
main()