before微应用

64 阅读1分钟

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()