Browse Source

update

main
wangshaoping 1 year ago
parent
commit
174b42a687
  1. 0
      app.platform/x.txt
  2. BIN
      io.sc.engine.mv.frontend/public/webjars/luckysheet/2.1.13/css/waffle_sprite.png
  3. 32
      io.sc.engine.mv.frontend/util-components-generator.cjs
  4. 173
      io.sc.engine.mv.frontend/util-frontend-register.cjs
  5. 159
      io.sc.engine.mv.frontend/webpack.config.common.cjs
  6. 66
      io.sc.engine.mv.frontend/webpack.config.mf.cjs
  7. 76
      io.sc.engine.mv.frontend/webpack.env.build.cjs
  8. 35
      io.sc.engine.mv.frontend/webpack.env.prod.cjs
  9. 35
      io.sc.engine.mv.frontend/webpack.env.serve.cjs

0
app.platform/x.txt

BIN
io.sc.engine.mv.frontend/public/webjars/luckysheet/2.1.13/css/waffle_sprite.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

32
io.sc.engine.mv.frontend/util-components-generator.cjs

@ -0,0 +1,32 @@
/**
* 用于自动生成前端组件
* 通过 src/routes/routes.json 文件构建 src/components/index.ts 文件
*/
const fs = require('fs');
const Json5 =require('json5');
// 解析前端路由配置文件
const routesJson = Json5.parse(fs.readFileSync('./src/routes/routes.json', 'utf8'));
let content ='';
content +='/**\n';
content +=' * 此文件为自动生成文件,请勿修改\n';
content +=' */\n\n';
for(const route of routesJson){
const componentName =route.component.substring(route.component.lastIndexOf('.')+1);
const componentPath =route.componentPath;
content +=`import ${componentName} from '${componentPath}';\n`;
}
content +='\n';
content +='const localComponents = { \n';
for(const route of routesJson){
const componentName =route.component.substring(route.component.lastIndexOf('.')+1);
content +=`'${route.component}': ${componentName},\n`;
}
content +='}\n\n';
content +='export default localComponents;\n';
fs.writeFileSync('./src/components/index.ts', content);
console.info('components generated!');

173
io.sc.engine.mv.frontend/util-frontend-register.cjs

@ -0,0 +1,173 @@
/**
* 用于将前端模块注册到后端服务器
*/
const packageJson = require('./package.json');
const { ModuleFederationPlugin } = require('webpack').container;
const Server = require('webpack-dev-server');
const mf = require('./webpack.config.mf.cjs');
const fs = require('fs');
const http = require('http');
const https = require('https');
const Json5 =require('json5');
// 解析前端注册器配置文件
const frontendRegisterConfigure = Json5.parse(fs.readFileSync('./frontend-register.json', 'utf8'));
// 解析前端路由配置文件
const frontendRoutes =Json5.parse(fs.readFileSync('./src/routes/routes.json', 'utf8'));
/**
* 远程组件注册器类
*/
class RemoteFrontEndModuleRegister {
/**
* 构造函数,传入配置信息, 包括远程和本地服务器配置信息
* 配置信息定义格式如下:
* // 远程服务器配置信息
* remoteServerConfig: {
* protocol: 'http',
* host: 'localhost',
* port: 8080,
* path: '/api/system/frontend/regist',
* },
* // 本地服务器配置信息
* localServerConfig: {
* protocol: devServer.options.server.type,
* host: Server.internalIPSync("v4"),
* port: devServer.options.port,
* path: '/',
* }
* @param devServer webpack dev server 对象
*/
constructor(devServer) {
if (!devServer) {
throw new Error('webpack-dev-server is not defined');
}
this.devServer = devServer;
this.registSuccess = null;
this.remoteServerConfig = {
protocol: frontendRegisterConfigure.protocol,
host: frontendRegisterConfigure.host,
port: frontendRegisterConfigure.port,
path: frontendRegisterConfigure.path,
};
this.localServerConfig = {
protocol: devServer.options.server.type,
host: Server.internalIPSync("v4"),
port: devServer.options.port,
path: '/',
};
}
/**
* 周期性向服务器注册前端模块
* @param delay 延迟执行(单位:毫秒)
* @param interval 固定频率执行(单位:毫秒)
*/
regist(delay,interval) {
if(frontendRegisterConfigure.enable){
setTimeout(() => {
let remoteServerUrl = this.remoteServerConfig.protocol + '//' + this.remoteServerConfig.host + ':' + this.remoteServerConfig.port + this.remoteServerConfig.path;
console.info('regist frontend module to server --> ' + remoteServerUrl);
setInterval(this.doRegist.bind(this), delay);
}, delay);
}
}
/**
* 向服务器注册前端模块
*/
doRegist() {
const data = JSON.stringify(this.getRegistJson());
if (data) {
let request = this.getRequest(this.remoteServerConfig.protocol);
let This = this;
request.on('error', error => {
if (This.registSuccess == null || This.registSuccess) {
This.registSuccess = false;
console.error('regist frontend module to server, Failed!', error);
}
});
request.write(data);
request.end();
}
}
/**
* 获取前端模块的注册信息
* @returns 前端模块的注册信息
*/
getRegistJson() {
return {
protocol: this.localServerConfig.protocol,
host: this.localServerConfig.host,
port: this.localServerConfig.port,
contextPath: this.localServerConfig.contextPath,
name: packageJson.name,
components: this.getComponents(),
routes: frontendRoutes,
}
}
/**
* 获取前端模块的注册信息(组件集合)
* @returns 前端模块的注册信息(组件集合)
*/
getComponents() {
const plugins = mf.plugins;
for (let i = 0; i < plugins.length; i++) {
const plugin = plugins[i];
if (plugin instanceof ModuleFederationPlugin) {
const exposes = plugin._options.exposes;
if (exposes) {
const components = [];
let keyIndex = 0;
for (let key in exposes) {
components[keyIndex] = key;
keyIndex++;
}
return components;
}
}
}
return null;
}
/**
* 获取 http/https 请求
* @param {*} protocol 请求协议
*/
getRequest(protocol) {
let request = http;
if (protocol == 'https:') {
request = https;
}
let This = this;
return request.request({
protocol: this.remoteServerConfig.protocol + ":",
host: this.remoteServerConfig.host,
port: this.remoteServerConfig.port,
path: this.remoteServerConfig.path,
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
}, request => {
request.setEncoding('utf-8');
request.on('data', d => {
const data = JSON.parse(d);
if (data.code === 200) {
if (This.registSuccess == null || !This.registSuccess) {
This.registSuccess = true;
console.info('regist frontend module to server, Success!');
}
} else {
console.error('regist frontend module to server, Failed!', d);
}
})
});
}
}
module.exports = {
RemoteFrontEndModuleRegister
}

159
io.sc.engine.mv.frontend/webpack.config.common.cjs

@ -0,0 +1,159 @@
/**
* webpack 通用配置
*/
const path = require('path'); // path
const webpack = require('webpack'); // webpack
const json5 = require('json5'); // json5
const HtmlWebpackPlugin = require('html-webpack-plugin'); // webpack html 生成插件
const CopyWebpackPlugin = require('copy-webpack-plugin'); // webpack copy 插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); // 抽取 css 插件
const { VueLoaderPlugin } = require('vue-loader'); // vue loader 插件
const ESLintPlugin = require('eslint-webpack-plugin'); // eslint 插件
const packageJson = require('./package.json'); // package.json
const projectName =packageJson.name; // 项目名称
module.exports = {
// 入口文件
entry: './src/main',
// 输出
output: {
// 输出路径(为兼容后端和多个前端项目)
// 1. 兼容后端: 将 dist 目录作为资源目录, 其中 public 种的静态资源可以直接访问
// 2. 兼容多个前端项目: 每个项目发布到 public 目录下的唯一项目名称目录
path: path.resolve(__dirname, `dist/public/${projectName}`),
// 输出文件名
filename: `javascript/[name].[contenthash:5].js`,
// 指定发布路径,使用 auto 可具有更多灵活性
publicPath: 'auto',
// 每次构建时,首先删除 output.path 目录所有内容,保证每次得到最新的构建结果
clean: true,
},
module: {
rules: [
// babel(包含处理: typescript)
{
test: /\.(t|j)s$/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader",
options: {
cacheDirectory: true,
}
}
]
},
// css
{
test: /\.(sa|sc|c)ss$/,
use: [{
loader: MiniCssExtractPlugin.loader,
},
{
loader: 'css-loader',
},
{
loader: 'postcss-loader',
}]
},
// 字体文件
{
test: /\.(woff|woff2|eot|ttf|otf)(\?.*)?$/,
type: 'asset/resource',
generator: {
filename: `fonts/[name].[contenthash:5].[ext]`,
}
},
// json5
{
test: /\.json$/,
type: 'json',
parser: {
parse: json5.parse,
},
},
// vue loader
{
test: /\.vue$/,
exclude: /node_modules/,
use: [
{
loader: 'vue-loader',
}
]
},
],
},
// 插件
plugins: [
new webpack.DefinePlugin({
__VUE_OPTIONS_API__: JSON.stringify(true),
__VUE_PROD_DEVTOOLS__: JSON.stringify(false)
}),
// 进度显示插件
new webpack.ProgressPlugin(),
// css 抽取插件
new MiniCssExtractPlugin({
filename: `css/[name].[contenthash:5].css`,
chunkFilename: `css/[name].[contenthash:5].css`
}),
// 自动生成静态 index.html 文件
new HtmlWebpackPlugin({
template: 'public/index.html',
filename: `index.html`,
minify: false,
inject: 'body',
timestamp: new Date().getTime(),
}),
// 拷贝静态资源到 output.path 指定的目录
new CopyWebpackPlugin({
patterns: [
{
from: 'public',
toType: 'dir',
filter: async (resourcePath) => {
// 不复制 index.html 因为 index.html 已经由 HtmlWebpackPlugin 插件生成了
if (resourcePath.endsWith('index.html') || resourcePath.endsWith('.DS_Store')) {
return false;
}
return true;
},
info: { minimized: true },
}
]
}),
// vue loader 插件
new VueLoaderPlugin(),
// eslint 插件
new ESLintPlugin({
fix: true,
formatter: 'stylish',
extensions: ['js', 'ts', 'vue', 'cjs'],
exclude: [
'node_modules',
],
}),
],
// 配置模块如何被解析,
resolve: {
// 设置模块别名,方便引用
alias: {
'@': path.resolve(__dirname, 'src'),
},
// 设置支持的模块扩展名,即这些扩展名的文件可以作为模块被使用
extensions: ['.ts', '.js', '.cjs', '.vue']
},
};

66
io.sc.engine.mv.frontend/webpack.config.mf.cjs

@ -0,0 +1,66 @@
/**
* webpack module federation 配置
*/
const fs = require('fs'); // 文件读取
const Json5 =require('json5'); // json5
const { ModuleFederationPlugin } = require('webpack').container; // webpack 模块联邦插件
const packageJson = require('./package.json'); // package.json
const projectName =packageJson.name; // 项目名称
const deps = packageJson.dependencies; // 项目依赖
// 读取本地路由配置, 通过其中 component 和 componentPath 两个属性构建 webpack 模块联邦的 exposes 属性值
const data = fs.readFileSync('./src/routes/routes.json', 'utf8');
const routes =Json5.parse(data);
const mfExposes ={};
for(const route of routes){
mfExposes[route.component]= route.componentPath;
}
// 导出 webapck 配置的模块联邦部分
module.exports = {
plugins: [
new ModuleFederationPlugin({
// 模块联邦的模块名称
name: `${projectName}`,
// 模块联邦的远程入口文件
filename: `javascript/remoteEntry.js`,
// 通过浏览器 window 对象保存模块联邦对象
library: { type: 'window', name: `${projectName}` },
remoteType: 'window',
// 模块联邦的导出组件
exposes: mfExposes,
// 模块联邦共享库
shared: {
'@codemirror/autocomplete': { requiredVersion: deps['@codemirror/autocomplete'], singleton: true },
'@codemirror/commands': { requiredVersion: deps['@codemirror/commands'], singleton: true },
'@codemirror/lang-html': { requiredVersion: deps['@codemirror/lang-html'], singleton: true },
'@codemirror/lang-java': { requiredVersion: deps['@codemirror/lang-java'], singleton: true },
'@codemirror/lang-javascript': { requiredVersion: deps['@codemirror/lang-javascript'], singleton: true },
'@codemirror/lang-json': { requiredVersion: deps['@codemirror/lang-json'], singleton: true },
'@codemirror/lang-sql': { requiredVersion: deps['@codemirror/lang-sql'], singleton: true },
'@codemirror/lang-xml': { requiredVersion: deps['@codemirror/lang-xml'], singleton: true },
'@codemirror/language': { requiredVersion: deps['@codemirror/language'], singleton: true },
'@codemirror/search': { requiredVersion: deps['@codemirror/search'], singleton: true },
'@codemirror/state': { requiredVersion: deps['@codemirror/state'], singleton: true },
'@codemirror/view': { requiredVersion: deps['@codemirror/view'], singleton: true },
'@vueuse/core': { requiredVersion: deps['@vueuse/core'], singleton: true },
'axios': { requiredVersion: deps['axios'], singleton: true },
'codemirror': { requiredVersion: deps['codemirror'], singleton: true },
'dayjs': { requiredVersion: deps['dayjs'], singleton: true },
'echarts':{ requiredVersion: deps['echarts'], singleton: true },
'exceljs':{ requiredVersion: deps['exceljs'], singleton: true },
'file-saver':{ requiredVersion: deps['file-saver'], singleton: true },
'luckyexcel':{ requiredVersion: deps['luckyexcel'], singleton: true },
"mockjs": { requiredVersion: deps['mockjs'], singleton: true },
'pinia': { requiredVersion: deps['pinia'], singleton: true },
'platform-core': { requiredVersion: deps['platform-core'], singleton: true },
'quasar': { requiredVersion: deps['quasar'], singleton: true },
'vue': { requiredVersion: deps['vue'], singleton: true },
'vue-codemirror6': { requiredVersion: deps['vue-codemirror6'], singleton: true },
'vue-dompurify-html':{ requiredVersion: deps['vue-dompurify-html'], singleton: true },
'vue-i18n': { requiredVersion: deps['vue-i18n'], singleton: true },
'vue-router': { requiredVersion: deps['vue-router'], singleton: true },
}
}),
]
};

76
io.sc.engine.mv.frontend/webpack.env.build.cjs

@ -0,0 +1,76 @@
/**
* 开发环境构建
*/
const { merge } = require('webpack-merge'); // webpack 配置合并函数
const common = require('./webpack.config.common.cjs'); // webpack 通用配置
const mf = require('./webpack.config.mf.cjs'); // webpack 模块联邦配置
module.exports = merge(common, mf, {
mode: 'development',
// -------------------------------------------------------------------------------------------------------------------------------
// devtool | performance | comment
// (none) | build:fastest, rebuild:fastest | Recommended choice for production builds with maximum performance.
// eval | build:fast, rebuild:fastest | Recommended choice for development builds with maximum performance.
// eval-source-map| build:slowest, rebuild:ok | Recommended choice for development builds with high quality SourceMaps.
// source-map | build:slowest, rebuild:slowest | Recommended choice for production builds with high quality SourceMaps.
// -------------------------------------------------------------------------------------------------------------------------------
devtool: 'eval-source-map',
optimization: {
minimize: false,
moduleIds: 'named',
chunkIds: 'named',
splitChunks: {
cacheGroups: {
'vue': {
name: 'vue',
test: /[\\/]node_modules[\\/](vue|vue-dompurify-html|vue-i18n|vue-router)[\\/]/,
priority: 20,
chunks: 'all',
enforce: true
},
'dnd':{
name: 'dnd',
test: /[\\/]node_modules[\\/](vue3-dnd|react-dnd-html5-backend|@vueuse[\\/]core)[\\/]/,
priority: 20,
chunks: 'all',
enforce: true
},
'quasar': {
name: 'quasar',
test: /[\\/]node_modules[\\/](quasar)[\\/]/,
priority: 20,
chunks: 'all',
enforce: true
},
'excel': {
name: 'excel',
test: /[\\/]node_modules[\\/](exceljs|luckyexcel|)[\\/]/,
priority: 20,
chunks: 'all',
enforce: true
},
'platform-core': {
name: 'platform-core',
test: /[\\/]node_modules[\\/]platform-core[\\/]/,
priority: 20,
chunks: 'all',
enforce: true
},
'view': {
name: 'view',
test: /[\\/]view[\\/]/,
priority: 20,
chunks: 'all',
enforce: true
},
'vendors': {
name: 'vendors',
test: /[\\/]node_modules[\\/]/,
chunks: 'all',
enforce: true
},
}
}
},
});

35
io.sc.engine.mv.frontend/webpack.env.prod.cjs

@ -0,0 +1,35 @@
/**
* 生产环境构建
*/
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); // css 压缩插件
const TerserPlugin = require("terser-webpack-plugin"); // js 压缩插件
const { merge } = require('webpack-merge'); // webpack 配置合并函数
const build = require('./webpack.env.build.cjs'); // 开发环境构建配置
module.exports = merge(build, {
mode: 'production',
// -------------------------------------------------------------------------------------------------------------------------------
// devtool | performance | comment
// (none) | build:fastest, rebuild:fastest | Recommended choice for production builds with maximum performance.
// eval | build:fast, rebuild:fastest | Recommended choice for development builds with maximum performance.
// eval-source-map| build:slowest, rebuild:ok | Recommended choice for development builds with high quality SourceMaps.
// source-map | build:slowest, rebuild:slowest | Recommended choice for production builds with high quality SourceMaps.
// -------------------------------------------------------------------------------------------------------------------------------
devtool: 'source-map',
optimization: {
minimize: true,
minimizer: [
new CssMinimizerPlugin(), // css 压缩插件
new TerserPlugin({ // js 压缩插件
extractComments: false,
terserOptions: {
format: {
comments: false,
},
},
}),
],
moduleIds: 'named',
chunkIds: 'named',
},
});

35
io.sc.engine.mv.frontend/webpack.env.serve.cjs

@ -0,0 +1,35 @@
/**
* 开发环境下启动 webpack dev server
*/
const path = require('path'); // path
const { merge } = require('webpack-merge'); // webpack 配置合并函数
const common = require('./webpack.config.common.cjs'); // webpack 通用配置
const mf = require('./webpack.config.mf.cjs'); // webpack 模块联邦配置
const { RemoteFrontEndModuleRegister } = require('./util-frontend-register.cjs'); // 远程模块注册器
module.exports = (env)=> merge(common, mf,{
mode: 'development',
devtool: 'eval',
devServer: {
client: {
overlay: false,
},
static: {
directory: path.join(__dirname, 'public'),
},
compress: false,
port: 3000,
hot: true,
// 保证在出现 404 错误时,能够导航到 index.html
historyApiFallback: true,
setupMiddlewares: (middlewares, devServer) => {
// 注册前端模块到远程服务器
const register = new RemoteFrontEndModuleRegister(devServer);
// 延后 5 秒执行, 且每 5 秒执行一次
register.regist(5000,5000);
return middlewares;
}
},
});
Loading…
Cancel
Save