🐛 build文件确实

This commit is contained in:
bunny 2025-06-04 13:29:15 +08:00
parent f95dc4d947
commit 42f1a7edfe
11 changed files with 404 additions and 1 deletions

1
.gitignore vendored
View File

@ -61,7 +61,6 @@ lerna-debug.log*
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/

View File

@ -0,0 +1,53 @@
import type { BuildOptions } from 'vite';
import { pathResolve } from './utils';
export const buildEnv = (): BuildOptions => {
return {
target: 'es2015',
assetsInlineLimit: 20000,
// 构建输出的目录,默认值为"dist"
outDir: 'docker/dist',
// 用于指定使用的代码压缩工具。在这里minify 被设置为 'terser',表示使用 Terser 进行代码压缩。默认值terser
// esbuild 打包更快,但是不能去除 console.logterser打包慢但能去除 console.log
minify: 'terser', // "esbuild"
// 用于配置 Terser 的选项
terserOptions: {
// 用于配置压缩选项
compress: {
drop_console: true, // 是否删除代码中的 console 语句, 默认值false
drop_debugger: true, // 是否删除代码中的 debugger 语句, 默认值false
},
},
// 禁用 gzip 压缩大小报告,可略微减少打包时间
reportCompressedSize: false,
// 用于指定是否生成源映射文件。源映射文件可以帮助调试和定位源代码中的错误。当设置为false时构建过程不会生成源映射文件
sourcemap: false,
// 用于配置 CommonJS 模块的选项
commonjsOptions: {
// 用于指定是否忽略 CommonJS 模块中的 try-catch 语句。当设置为false时构建过程会保留 CommonJS 模块中的 try-catch 语句
ignoreTryCatch: false,
},
// 规定触发警告的 chunk 大小, 当某个代码分块的大小超过该限制时Vite 会发出警告
chunkSizeWarningLimit: 2000,
rollupOptions: {
external: ['md-editor-v3', 'echarts'],
input: {
// @ts-ignore
index: pathResolve('../index.html', import.meta.url),
},
// 静态资源分类打包
output: {
chunkFileNames: 'static/js/[name]-[hash].js',
entryFileNames: 'static/js/[name]-[hash].js',
assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
manualChunks: (id) => {
// 如果是包含在包中则打包成 vendor
if (id.includes('node_modules')) {
return `vendor`;
}
},
},
},
};
};

View File

@ -0,0 +1,47 @@
import { Plugin as importToCDN } from 'vite-plugin-cdn-import';
import { wrapperEnv } from './utils';
/**
* @description `cdn`使cdn模式 .env.production VITE_CDN true
* cdnhttps://www.bootcdn.cn当然你也可以选择 https://unpkg.com 或者 https://www.jsdelivr.com
* 使jscss文件cdn
*/
export const cdn = importToCDN({
//prodUrl解释 name: 对应下面modules的nameversion: 自动读取本地package.json中dependencies依赖中对应包的版本号path: 对应下面modules的path当然也可写完整路径会替换prodUrl
// prodUrl: 'https://cdn.bootcdn.net/ajax/libs/{name}/{version}/{path}',
prodUrl: 'https://unpkg.com/{name}@{version}/{path}',
modules: [
{
name: 'vue',
var: 'Vue',
path: 'dist/vue.global.prod.js',
},
{
name: 'vue-router',
var: 'VueRouter',
path: 'dist/vue-router.global.js',
},
{
name: 'pinia',
var: 'Pinia',
path: 'dist/pinia.iife.js',
},
{
name: 'axios',
var: 'axios',
path: 'dist/axios.min.js',
},
{
name: 'dayjs',
var: 'dayjs',
path: 'dayjs.min.js',
},
],
});
/* 是否使用CDN加速 */
export const useCDN = (mode) => {
const env = wrapperEnv(mode, 'VITE');
return env.VITE_CDN ? cdn : null;
};

View File

@ -0,0 +1,12 @@
import type { CSSOptions } from 'vite';
export const css = (mode: string): CSSOptions => {
return {
preprocessorOptions: {
scss: {
additionalData: `@use "@/assets/styles/minix/sidebar" as *;`,
},
},
};
};

View File

@ -0,0 +1,14 @@
import dayjs from 'dayjs';
import { dependencies, devDependencies, engines, name, version } from '../package.json';
const __APP_INFO__ = {
pkg: { name, version, engines, dependencies, devDependencies },
lastBuildTime: dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss'),
};
export const define = () => {
return {
__APP_INFO__: JSON.stringify(__APP_INFO__),
};
};

View File

@ -0,0 +1,58 @@
import boxen, { type Options as BoxenOptions } from 'boxen';
import dayjs, { type Dayjs } from 'dayjs';
import duration from 'dayjs/plugin/duration';
import gradientString from 'gradient-string';
import { logOutputSize, wrapperEnv } from './utils';
dayjs.extend(duration);
const boxenOptions: BoxenOptions = {
padding: 0.94,
borderColor: 'cyan',
borderStyle: 'round',
textAlignment: 'left',
};
/* 输出日志信息 */
const printLogMessage = (VITE_PORT: number) => {
return gradientString('cyan', 'magenta').multiline(
`欢迎使用此项目,项目访问地址如下:
http://localhost:${VITE_PORT}`
);
};
export const viteConsoleLog = (mode: string) => {
const { VITE_PORT } = wrapperEnv(mode);
let config: { command: string };
let startTime: Dayjs;
let endTime: Dayjs;
return {
name: 'vite:buildInfo',
configResolved(resolvedConfig) {
config = resolvedConfig;
},
buildStart() {
console.log(boxen(printLogMessage(VITE_PORT), boxenOptions));
if (config.command === 'build') {
startTime = dayjs(new Date());
}
},
closeBundle() {
if (config.command === 'build') {
endTime = dayjs(new Date());
const format = dayjs.duration(endTime.diff(startTime)).format('mm分ss秒');
console.log(
boxen(
gradientString('cyan', 'magenta').multiline(
`🎉 恭喜打包完成(总用时${format})打包大小(${logOutputSize()}`
),
boxenOptions
)
);
}
},
};
};

View File

@ -0,0 +1,14 @@
/**
* `vite.config.ts` `optimizeDeps.include`
* `vite` include esm node_modules/.vite
* include里vite 使 node_modules/.vite
* 使 src/main.ts include vite node_modules/.vite
*/
const include = ['vue', 'vue-router', 'dayjs', 'axios', 'pinia', 'vue-types', 'js-cookie'];
/**
*
*/
const exclude: string[] = [];
export { exclude, include };

View File

@ -0,0 +1,45 @@
import UnoCssIcons from '@unocss/preset-icons';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import { presetIcons } from 'unocss';
import UnoCSS from 'unocss/vite';
import type { PluginOption } from 'vite';
import removeConsole from 'vite-plugin-remove-console';
import Inspector from 'vite-plugin-vue-inspector';
import { useCDN } from './cdn';
import { viteConsoleLog } from './info';
import { compressPack, report } from './utils';
export const plugins = (mode: string): PluginOption[] => {
return [
vue(),
vueJsx(),
Inspector(),
report(),
removeConsole(),
useCDN(mode),
viteConsoleLog(mode),
UnoCSS({
hmrTopLevelAwait: false,
inspector: true, // 控制台是否打印 UnoCSS inspector
presets: [
presetIcons({
prefix: '',
extraProperties: {
display: 'inline-block',
'vertical-align': 'middle',
},
}),
UnoCssIcons({
prefix: '',
extraProperties: {
display: 'inline-block',
'vertical-align': 'middle',
},
}),
],
}),
compressPack(mode),
];
};

View File

@ -0,0 +1,9 @@
import { pathResolve } from './utils';
export const resolve = () => {
return {
alias: {
'@': pathResolve('../src'),
},
};
};

View File

@ -0,0 +1,34 @@
import type { ServerOptions } from 'vite';
import { wrapperEnv } from './utils';
/* 开发服务配置 */
export const server = (mode: string) => {
const { VITE_PORT, VITE_APP_URL, VITE_STRICT_PORT } = wrapperEnv(mode);
const options: ServerOptions = {
strictPort: VITE_STRICT_PORT,
port: VITE_PORT,
host: '0.0.0.0',
open: true,
cors: true,
proxy: {
'/api': {
target: VITE_APP_URL,
changeOrigin: true,
rewrite: (path: string) => path.replace(/^\/api/, '/api'),
},
'/mock': {
target: VITE_APP_URL,
changeOrigin: true,
rewrite: (path: string) => path.replace(/^\/mock/, '/mock'),
},
},
// 预热文件以提前转换和缓存结果,降低启动期间的初始页面加载时长并防止转换瀑布
warmup: {
clientFiles: ['./index.html', './src/{views,components}/*'],
},
};
return options;
};

View File

@ -0,0 +1,118 @@
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import fs from 'fs';
import path from 'path';
import { visualizer } from 'rollup-plugin-visualizer';
import { loadEnv } from 'vite';
import viteCompression from 'vite-plugin-compression';
import { buildEnv } from './buildEnv';
export const root: string = process.cwd();
/**
* @description
* @param dir `build`
* @param metaUrl `url``build``import.meta.url`
*/
// @ts-ignore
export const pathResolve = (dir = '.', metaUrl = import.meta.url) => {
// 当前文件目录的绝对路径
const currentFileDir = dirname(fileURLToPath(metaUrl));
// build 目录的绝对路径
const buildDir = resolve(currentFileDir, 'build');
// 解析的绝对路径
const resolvedPath = resolve(currentFileDir, dir);
// 检查解析的绝对路径是否在 build 目录内
if (resolvedPath.startsWith(buildDir)) {
// 在 build 目录内,返回当前文件路径
return fileURLToPath(metaUrl);
}
// 不在 build 目录内,返回解析后的绝对路径
return resolvedPath;
};
/**
*
* @param mode
* @param prefix
* @link https://cn.vite.dev/config/#using-environment-variables-in-config
*/
// @ts-ignore
export const wrapperEnv = (mode: string, prefix: string = ''): ViteEnv => {
const env: any = loadEnv(mode, root, prefix);
// 将变量转换指定类型
for (const envName of Object.keys(env)) {
let realName: string | boolean | number = env[envName].replace(/\\n/g, '\n');
realName = realName === 'true' ? true : realName === 'false' ? false : realName;
if (envName === 'VITE_PORT') {
realName = Number(realName);
}
env[envName] = realName;
// @ts-ignore
process.env[envName] = realName;
}
return env;
};
/* 打包分析 */
export const report = () => {
const lifecycle = process.env.npm_lifecycle_event;
return lifecycle === 'report'
? visualizer({ open: true, brotliSize: true, filename: 'report.html' })
: (null as any);
};
/* 启用gzip压缩 */
export const compressPack = (mode: string) => {
const { VITE_COMPRESSION } = wrapperEnv(mode);
return VITE_COMPRESSION == 'gzip' ? viteCompression({ threshold: 1024000 }) : null;
};
/**
*
* @returns
*/
export const logOutputSize = (): string => {
const outDir = `../${buildEnv().outDir}`;
function convertSize(size: number) {
const units: Array<string> = ['byte', 'KB', 'MB', 'GB'];
// 输入的单位是否存在
let index = 0;
while (size >= 1024) {
size /= 1024;
index++;
}
return `${size.toFixed(2)} ${units[index]}`;
}
// 计算文件夹字节大小
function getFolderSize(folderPath: string) {
let size = 0;
fs.readdirSync(folderPath).forEach((fileName: string) => {
const filePath = path.join(folderPath, fileName);
const stats = fs.statSync(filePath);
if (stats.isFile()) {
size += stats.size;
} else if (stats.isDirectory()) {
size += getFolderSize(filePath);
}
});
return size;
}
const folderSize = getFolderSize(path.resolve(__dirname, outDir));
return convertSize(folderSize);
};