perf(web): 降低生产构建内存峰值,并启用 CI 低内存打包模式
关闭按需 Element Plus 样式与构建期 dts、限制 rollup 并行、CI 跳过 gzip; 新增 npm run build:ci,供 Drone 在小内存机器上打包。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
df6ce133dd
commit
9d93a373ae
8 files changed
+55
-27
No files matched your search
+6
-3
@@ -37,17 +37,20 @@ steps:
|
|||||||
|
|
||||||
- name: package-web
|
- name: package-web
|
||||||
# 无 package-lock(被 gitignore);Node20 需 --legacy-peer-deps 规避 npm edgesOut
|
# 无 package-lock(被 gitignore);Node20 需 --legacy-peer-deps 规避 npm edgesOut
|
||||||
|
# 低内存模式:串行 transform、关 gzip 插件、3G 堆(见 web build:ci)
|
||||||
image: node:20-bookworm
|
image: node:20-bookworm
|
||||||
environment:
|
environment:
|
||||||
NPM_CONFIG_REGISTRY: https://registry.npmmirror.com
|
NPM_CONFIG_REGISTRY: https://registry.npmmirror.com
|
||||||
|
CI: 'true'
|
||||||
|
VITE_LOW_MEM: '1'
|
||||||
|
VITE_BUILD_COMPRESS: ''
|
||||||
commands:
|
commands:
|
||||||
- set -eu
|
- set -eu
|
||||||
- echo "==> web tag=${DRONE_TAG} npm=$(npm -v) node=$(node -v)"
|
- echo "==> web tag=${DRONE_TAG} npm=$(npm -v) node=$(node -v) low_mem=${VITE_LOW_MEM}"
|
||||||
- mkdir -p "/hzdyjs-releases/${DRONE_TAG}"
|
- mkdir -p "/hzdyjs-releases/${DRONE_TAG}"
|
||||||
- cd web
|
- cd web
|
||||||
- npm install --legacy-peer-deps --no-audit --no-fund
|
- npm install --legacy-peer-deps --no-audit --no-fund
|
||||||
# 本前端生产构建实测需约 6G+ 堆;测试机靠扩容 swap(/swap-hzdyjs)硬扛
|
- npm run build:ci
|
||||||
- node --max-old-space-size=6144 node_modules/vite/bin/vite.js build --mode production
|
|
||||||
- test -f dist/index.html
|
- test -f dist/index.html
|
||||||
- tar -C dist -czf "/hzdyjs-releases/${DRONE_TAG}/web-${DRONE_TAG}.tar.gz" .
|
- tar -C dist -czf "/hzdyjs-releases/${DRONE_TAG}/web-${DRONE_TAG}.tar.gz" .
|
||||||
- ls -lh "/hzdyjs-releases/${DRONE_TAG}/web-${DRONE_TAG}.tar.gz"
|
- ls -lh "/hzdyjs-releases/${DRONE_TAG}/web-${DRONE_TAG}.tar.gz"
|
||||||
|
|||||||
+3
-2
@@ -8,8 +8,9 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite serve --mode development",
|
"dev": "vite serve --mode development",
|
||||||
"build:prod": "node --max-old-space-size=8192 node_modules/vite/bin/vite.js build --mode production",
|
"build:prod": "node --max-old-space-size=6144 node_modules/vite/bin/vite.js build --mode production",
|
||||||
"build:dev": "node --max-old-space-size=8192 node_modules/vite/bin/vite.js build --mode development",
|
"build:ci": "CI=true VITE_LOW_MEM=1 VITE_BUILD_COMPRESS= node --max-old-space-size=3072 node_modules/vite/bin/vite.js build --mode production",
|
||||||
|
"build:dev": "node --max-old-space-size=6144 node_modules/vite/bin/vite.js build --mode development",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"lint:eslint": "eslint",
|
"lint:eslint": "eslint",
|
||||||
"lint:eslint:fix": "eslint --fix",
|
"lint:eslint:fix": "eslint --fix",
|
||||||
|
|||||||
+22
-3
@@ -5,6 +5,9 @@ import path from 'path';
|
|||||||
|
|
||||||
export default defineConfig(({ mode, command }) => {
|
export default defineConfig(({ mode, command }) => {
|
||||||
const env = loadEnv(mode, process.cwd());
|
const env = loadEnv(mode, process.cwd());
|
||||||
|
const isBuild = command === 'build';
|
||||||
|
// CI / 小内存机器:限制并行、跳过 gzip 插件(产物外层已 tar.gz)
|
||||||
|
const lowMem = process.env.CI === 'true' || process.env.VITE_LOW_MEM === '1';
|
||||||
return {
|
return {
|
||||||
// 部署生产环境和开发环境下的URL。
|
// 部署生产环境和开发环境下的URL。
|
||||||
// 默认情况下,vite 会假设你的应用是被部署在一个域名的根路径上
|
// 默认情况下,vite 会假设你的应用是被部署在一个域名的根路径上
|
||||||
@@ -17,7 +20,7 @@ export default defineConfig(({ mode, command }) => {
|
|||||||
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
|
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
|
||||||
},
|
},
|
||||||
// https://cn.vitejs.dev/config/#resolve-extensions
|
// https://cn.vitejs.dev/config/#resolve-extensions
|
||||||
plugins: createPlugins(env, command === 'build'),
|
plugins: createPlugins(env, isBuild),
|
||||||
server: {
|
server: {
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
port: Number(env.VITE_APP_PORT),
|
port: Number(env.VITE_APP_PORT),
|
||||||
@@ -54,6 +57,23 @@ export default defineConfig(({ mode, command }) => {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
esbuild: {
|
||||||
|
legalComments: 'none'
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
sourcemap: false,
|
||||||
|
// 控制台 gzip 体积统计会再压缩一遍全部产物,大项目很吃内存
|
||||||
|
reportCompressedSize: false,
|
||||||
|
chunkSizeWarningLimit: 2000,
|
||||||
|
// esbuild 比 terser 省内存、更快
|
||||||
|
minify: 'esbuild',
|
||||||
|
cssMinify: 'esbuild',
|
||||||
|
target: 'es2020',
|
||||||
|
// 小内存 / CI:串行读文件,显著降低 transform 峰值
|
||||||
|
rollupOptions: {
|
||||||
|
maxParallelFileOps: lowMem ? 1 : 2
|
||||||
|
}
|
||||||
|
},
|
||||||
// 预编译
|
// 预编译
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
include: [
|
include: [
|
||||||
@@ -65,8 +85,7 @@ export default defineConfig(({ mode, command }) => {
|
|||||||
'echarts',
|
'echarts',
|
||||||
'vue-i18n',
|
'vue-i18n',
|
||||||
'@vueup/vue-quill',
|
'@vueup/vue-quill',
|
||||||
'image-conversion',
|
'image-conversion'
|
||||||
'element-plus/es/components/**/css'
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
import AutoImport from 'unplugin-auto-import/vite';
|
import AutoImport from 'unplugin-auto-import/vite';
|
||||||
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
|
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
|
||||||
|
|
||||||
export default (path: any) => {
|
export default (path: any, isBuild = false) => {
|
||||||
return AutoImport({
|
return AutoImport({
|
||||||
// 自动导入 Vue 相关函数
|
// 自动导入 Vue 相关函数
|
||||||
imports: ['vue', 'vue-router', '@vueuse/core', 'pinia'],
|
imports: ['vue', 'vue-router', '@vueuse/core', 'pinia'],
|
||||||
eslintrc: {
|
eslintrc: {
|
||||||
enabled: true,
|
// 生产构建不重写 eslint 全局声明,避免每次打包全量扫描
|
||||||
|
enabled: !isBuild,
|
||||||
filepath: './.eslintrc-auto-import.json',
|
filepath: './.eslintrc-auto-import.json',
|
||||||
globalsPropValue: true
|
globalsPropValue: true
|
||||||
},
|
},
|
||||||
resolvers: [
|
resolvers: [
|
||||||
// 自动导入 Element Plus 相关函数ElMessage, ElMessageBox... (带样式)
|
// 自动导入 Element Plus 相关函数ElMessage, ElMessageBox... (带样式)
|
||||||
ElementPlusResolver()
|
ElementPlusResolver({ importStyle: false })
|
||||||
],
|
],
|
||||||
vueTemplate: true, // 是否在 vue 模板中自动导入
|
vueTemplate: true, // 是否在 vue 模板中自动导入
|
||||||
dts: path.resolve(path.resolve(__dirname, '../../src'), 'types', 'auto-imports.d.ts')
|
dts: isBuild ? false : path.resolve(path.resolve(__dirname, '../../src'), 'types', 'auto-imports.d.ts')
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -2,16 +2,18 @@ import Components from 'unplugin-vue-components/vite';
|
|||||||
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
|
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
|
||||||
import IconsResolver from 'unplugin-icons/resolver';
|
import IconsResolver from 'unplugin-icons/resolver';
|
||||||
|
|
||||||
export default (path: any) => {
|
export default (path: any, isBuild = false) => {
|
||||||
return Components({
|
return Components({
|
||||||
resolvers: [
|
resolvers: [
|
||||||
// 自动导入 Element Plus 组件
|
// 自动导入 Element Plus 组件
|
||||||
ElementPlusResolver(),
|
// 全局已引入 element-plus/dist/index.css,关闭按需样式,减少打包模块数
|
||||||
|
ElementPlusResolver({ importStyle: false }),
|
||||||
// 自动注册图标组件
|
// 自动注册图标组件
|
||||||
IconsResolver({
|
IconsResolver({
|
||||||
enabledCollections: ['ep']
|
enabledCollections: ['ep']
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
dts: path.resolve(path.resolve(__dirname, '../../src'), 'types', 'components.d.ts')
|
// 生产构建不重写声明文件,避免把全部组件信息留在内存里
|
||||||
|
dts: isBuild ? false : path.resolve(path.resolve(__dirname, '../../src'), 'types', 'components.d.ts')
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -1,16 +1,20 @@
|
|||||||
import compression from 'vite-plugin-compression';
|
import compression from 'vite-plugin-compression';
|
||||||
|
|
||||||
export default (env: any) => {
|
export default (env: any) => {
|
||||||
const { VITE_BUILD_COMPRESS } = env;
|
// CI 可通过空值或空格关闭:VITE_BUILD_COMPRESS=
|
||||||
|
const raw = String(env?.VITE_BUILD_COMPRESS ?? '').trim();
|
||||||
const plugin: any[] = [];
|
const plugin: any[] = [];
|
||||||
if (VITE_BUILD_COMPRESS) {
|
if (raw) {
|
||||||
const compressList = VITE_BUILD_COMPRESS.split(',');
|
const compressList = raw.split(',').map((s: string) => s.trim()).filter(Boolean);
|
||||||
if (compressList.includes('gzip')) {
|
if (compressList.includes('gzip')) {
|
||||||
// http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件
|
// http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件
|
||||||
plugin.push(
|
plugin.push(
|
||||||
compression({
|
compression({
|
||||||
ext: '.gz',
|
ext: '.gz',
|
||||||
deleteOriginFile: false
|
deleteOriginFile: false,
|
||||||
|
// 小于 10KB 的分包压缩收益很小,跳过可明显缩短收尾时间
|
||||||
|
threshold: 10 * 1024,
|
||||||
|
verbose: false
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import Icons from 'unplugin-icons/vite';
|
import Icons from 'unplugin-icons/vite';
|
||||||
|
|
||||||
export default () => {
|
export default (isBuild = false) => {
|
||||||
return Icons({
|
return Icons({
|
||||||
// 自动安装图标库
|
// 构建时禁止临时 npm install,避免打包过程访问外网卡住
|
||||||
autoInstall: true
|
autoInstall: !isBuild
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import vue from '@vitejs/plugin-vue';
|
import vue from '@vitejs/plugin-vue';
|
||||||
import vueDevTools from 'vite-plugin-vue-devtools'; // 临时禁用以解决localStorage兼容性问题
|
|
||||||
|
|
||||||
import createUnoCss from './unocss';
|
import createUnoCss from './unocss';
|
||||||
import createAutoImport from './auto-import';
|
import createAutoImport from './auto-import';
|
||||||
@@ -13,12 +12,11 @@ import path from 'path';
|
|||||||
export default (viteEnv: any, isBuild = false): [] => {
|
export default (viteEnv: any, isBuild = false): [] => {
|
||||||
const vitePlugins: any = [];
|
const vitePlugins: any = [];
|
||||||
vitePlugins.push(vue());
|
vitePlugins.push(vue());
|
||||||
// vitePlugins.push(vueDevTools()); // 禁用以解决localStorage兼容性问题
|
|
||||||
vitePlugins.push(createUnoCss());
|
vitePlugins.push(createUnoCss());
|
||||||
vitePlugins.push(createAutoImport(path));
|
vitePlugins.push(createAutoImport(path, isBuild));
|
||||||
vitePlugins.push(createComponents(path));
|
vitePlugins.push(createComponents(path, isBuild));
|
||||||
vitePlugins.push(createCompression(viteEnv));
|
vitePlugins.push(createCompression(viteEnv));
|
||||||
vitePlugins.push(createIcons());
|
vitePlugins.push(createIcons(isBuild));
|
||||||
vitePlugins.push(createSvgIconsPlugin(path));
|
vitePlugins.push(createSvgIconsPlugin(path));
|
||||||
vitePlugins.push(createSetupExtend());
|
vitePlugins.push(createSetupExtend());
|
||||||
return vitePlugins;
|
return vitePlugins;
|
||||||
|
|||||||
Reference in new issue
Block a user