feat: 🚀 打包配置优化
This commit is contained in:
parent
47e0b8d504
commit
4a12220476
|
@ -0,0 +1,50 @@
|
|||
import type { BuildOptions } from 'vite';
|
||||
|
||||
export const buildEnvironment = () => {
|
||||
const environment: BuildOptions = {
|
||||
assetsInlineLimit: 20000,
|
||||
// 构建输出的目录,默认值为"dist"
|
||||
outDir: 'dist',
|
||||
// 用于指定使用的代码压缩工具。在这里,minify 被设置为 'terser',表示使用 Terser 进行代码压缩。默认值terser
|
||||
// esbuild 打包更快,但是不能去除 console.log,terser打包慢,但能去除 console.log
|
||||
minify: 'terser',
|
||||
// 用于配置 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,
|
||||
// 用于配置 Rollup 打包工具的选项
|
||||
rollupOptions: {
|
||||
// 用于配置输出选项
|
||||
output: {
|
||||
// 静态资源分类和包装
|
||||
chunkFileNames: 'js/[name]-[hash].js', // 用于指定代码分块的输出文件名格式
|
||||
entryFileNames: 'js/[name]-[hash].js', // 用于指定入口文件的输出文件名格式
|
||||
assetFileNames: 'assets/[ext]/[name]-[hash].[ext]', // 用于指定静态资源的输出文件名格式
|
||||
// ? 配置文件生成方式
|
||||
manualChunks: (id, meta) => {
|
||||
// 如果是包含在包中则打包成 vendor
|
||||
if (id.includes('node_modules')) {
|
||||
return 'vendor';
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return environment;
|
||||
};
|
|
@ -0,0 +1,55 @@
|
|||
import { Plugin as importToCDN } from "vite-plugin-cdn-import";
|
||||
|
||||
/**
|
||||
* @description 打包时采用`cdn`模式,仅限外网使用(默认不采用,如果需要采用cdn模式,请在 .env.production 文件,将 VITE_CDN 设置成true)
|
||||
* 平台采用国内cdn:https://www.bootcdn.cn,当然你也可以选择 https://unpkg.com 或者 https://www.jsdelivr.com
|
||||
* 注意:上面提到的仅限外网使用也不是完全肯定的,如果你们公司内网部署的有相关js、css文件,也可以将下面配置对应改一下,整一套内网版cdn
|
||||
*/
|
||||
export const cdn = importToCDN({
|
||||
//(prodUrl解释: name: 对应下面modules的name,version: 自动读取本地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.js',
|
||||
},
|
||||
{
|
||||
name: 'vue-router',
|
||||
var: 'VueRouter',
|
||||
path: 'dist/vue-router.global.js',
|
||||
},
|
||||
{
|
||||
name: 'vue-i18n',
|
||||
var: 'VueI18n',
|
||||
path: 'dist/vue-i18n.global.js',
|
||||
},
|
||||
// 项目中没有直接安装vue-demi,但是pinia用到了,所以需要在引入pinia前引入vue-demi(https://github.com/vuejs/pinia/blob/v2/packages/pinia/package.json#L77)
|
||||
{
|
||||
name: 'vue-demi',
|
||||
var: 'VueDemi',
|
||||
path: 'lib/index.iife.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',
|
||||
},
|
||||
{
|
||||
name: 'echarts',
|
||||
var: 'echarts',
|
||||
path: 'dist/echarts.js',
|
||||
},
|
||||
],
|
||||
});
|
|
@ -0,0 +1,57 @@
|
|||
import type { Plugin } from 'vite';
|
||||
import { isArray } from '@pureadmin/utils';
|
||||
import compressPlugin from 'vite-plugin-compression';
|
||||
|
||||
export const configCompressPlugin = (compress: ViteCompression): Plugin | Plugin[] => {
|
||||
if (compress === 'none') return null;
|
||||
|
||||
const gz = {
|
||||
// 生成的压缩包后缀
|
||||
ext: '.gz',
|
||||
// 体积大于threshold才会被压缩
|
||||
threshold: 0,
|
||||
// 默认压缩.js|mjs|json|css|html后缀文件,设置成true,压缩全部文件
|
||||
filter: () => true,
|
||||
// 压缩后是否删除原始文件
|
||||
deleteOriginFile: false,
|
||||
};
|
||||
const br = {
|
||||
ext: '.br',
|
||||
algorithm: 'brotliCompress',
|
||||
threshold: 0,
|
||||
filter: () => true,
|
||||
deleteOriginFile: false,
|
||||
};
|
||||
|
||||
const codeList = [
|
||||
{ k: 'gzip', v: gz },
|
||||
{ k: 'brotli', v: br },
|
||||
{ k: 'both', v: [gz, br] },
|
||||
];
|
||||
|
||||
const plugins: Plugin[] = [];
|
||||
|
||||
codeList.forEach(item => {
|
||||
if (compress.includes(item.k)) {
|
||||
if (compress.includes('clear')) {
|
||||
if (isArray(item.v)) {
|
||||
item.v.forEach(vItem => {
|
||||
plugins.push(compressPlugin(Object.assign(vItem, { deleteOriginFile: true })));
|
||||
});
|
||||
} else {
|
||||
plugins.push(compressPlugin(Object.assign(item.v, { deleteOriginFile: true })));
|
||||
}
|
||||
} else {
|
||||
if (isArray(item.v)) {
|
||||
item.v.forEach(vItem => {
|
||||
plugins.push(compressPlugin(vItem));
|
||||
});
|
||||
} else {
|
||||
plugins.push(compressPlugin(item.v));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return plugins;
|
||||
};
|
|
@ -0,0 +1,53 @@
|
|||
import type { Plugin } from 'vite';
|
||||
import { getPackageSize } from './utils';
|
||||
import dayjs, { type Dayjs } from 'dayjs';
|
||||
import duration from 'dayjs/plugin/duration';
|
||||
import gradientString from 'gradient-string';
|
||||
import boxen, { type Options as BoxenOptions } from 'boxen';
|
||||
|
||||
dayjs.extend(duration);
|
||||
|
||||
const welcomeMessage = gradientString('cyan', 'magenta').multiline(
|
||||
`您好! 欢迎使用 bunny-admin 后台管理
|
||||
项目开发访问地址如下:
|
||||
http://localhost:8202/
|
||||
项目生产访问地址如下:
|
||||
http://localhost:8202/`,
|
||||
);
|
||||
|
||||
const boxenOptions: BoxenOptions = {
|
||||
padding: 0.5,
|
||||
borderColor: 'cyan',
|
||||
borderStyle: 'round',
|
||||
};
|
||||
|
||||
export function viteBuildInfo(): Plugin {
|
||||
let config: { command: string };
|
||||
let startTime: Dayjs;
|
||||
let endTime: Dayjs;
|
||||
let outDir: string;
|
||||
return {
|
||||
name: 'vite:buildInfo',
|
||||
configResolved(resolvedConfig) {
|
||||
config = resolvedConfig;
|
||||
outDir = resolvedConfig.build?.outDir ?? 'dist';
|
||||
},
|
||||
buildStart() {
|
||||
console.log(boxen(welcomeMessage, boxenOptions));
|
||||
if (config.command === 'build') {
|
||||
startTime = dayjs(new Date());
|
||||
}
|
||||
},
|
||||
closeBundle() {
|
||||
if (config.command === 'build') {
|
||||
endTime = dayjs(new Date());
|
||||
getPackageSize({
|
||||
folder: outDir,
|
||||
callback: (size: string) => {
|
||||
console.log(boxen(gradientString('cyan', 'magenta').multiline(`🎉 恭喜打包完成(总用时${dayjs.duration(endTime.diff(startTime)).format('mm分ss秒')},打包后的大小为${size})`), boxenOptions));
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
* 此文件作用于 `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 = [
|
||||
'qs',
|
||||
'mitt',
|
||||
'xlsx',
|
||||
'dayjs',
|
||||
'axios',
|
||||
'pinia',
|
||||
'typeit',
|
||||
'swiper',
|
||||
'qrcode',
|
||||
'intro.js',
|
||||
'vue-i18n',
|
||||
'vxe-table',
|
||||
'vue-types',
|
||||
'js-cookie',
|
||||
'vue-tippy',
|
||||
'cropperjs',
|
||||
'jsbarcode',
|
||||
'pinyin-pro',
|
||||
'sortablejs',
|
||||
'swiper/vue',
|
||||
'mint-filter',
|
||||
'@vueuse/core',
|
||||
'vue3-danmaku',
|
||||
'v-contextmenu',
|
||||
'vue-pdf-embed',
|
||||
'wavesurfer.js',
|
||||
'swiper/modules',
|
||||
'china-area-data',
|
||||
'vue-json-pretty',
|
||||
'@logicflow/core',
|
||||
'@pureadmin/utils',
|
||||
'@wangeditor/editor',
|
||||
'responsive-storage',
|
||||
'plus-pro-components',
|
||||
'@howdyjs/mouse-menu',
|
||||
'@logicflow/extension',
|
||||
'vue-virtual-scroller',
|
||||
'@amap/amap-jsapi-loader',
|
||||
'el-table-infinite-scroll',
|
||||
'vue-waterfall-plugin-next',
|
||||
'@infectoone/vue-ganttastic',
|
||||
'@wangeditor/editor-for-vue',
|
||||
'vuedraggable/src/vuedraggable',
|
||||
];
|
||||
|
||||
/**
|
||||
* 在预构建中强制排除的依赖项
|
||||
* 温馨提示:所有以 `@iconify-icons/` 开头引入的的本地图标模块,都应该加入到下面的 `exclude` 里,因为平台推荐的使用方式是哪里需要哪里引入而且都是单个的引入,不需要预构建,直接让浏览器加载就好
|
||||
*/
|
||||
const exclude = ['@iconify-icons/ep', '@iconify-icons/ri', '@pureadmin/theme/dist/browser-utils'];
|
||||
|
||||
export { include, exclude };
|
|
@ -0,0 +1,43 @@
|
|||
import { cdn } from './cdn';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import type { PluginOption } from 'vite';
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx';
|
||||
import legacy from '@vitejs/plugin-legacy';
|
||||
import { compression } from 'vite-plugin-compression2';
|
||||
import { viteMockServe } from 'vite-plugin-mock';
|
||||
import { viteBuildInfo } from './info';
|
||||
|
||||
export function getPluginsList(): PluginOption[] {
|
||||
const lifecycle = process.env.npm_lifecycle_event;
|
||||
return [
|
||||
vue(),
|
||||
legacy({
|
||||
targets: ['android 4', 'ios 8', 'chrome 30', 'ie 6'],
|
||||
additionalLegacyPolyfills: ['regenerator-runtime/runtime'],
|
||||
renderLegacyChunks: true,
|
||||
polyfills: [
|
||||
'es.symbol',
|
||||
'es.array.filter',
|
||||
'es.promise',
|
||||
'es.promise.finally',
|
||||
'es/map',
|
||||
'es/set',
|
||||
'es.array.for-each',
|
||||
'es.object.define-properties',
|
||||
'es.object.define-property',
|
||||
'es.object.get-own-property-descriptor',
|
||||
'es.object.get-own-property-descriptors',
|
||||
'es.object.keys',
|
||||
'es.object.to-string',
|
||||
'web.dom-collections.for-each',
|
||||
'esnext.global-this',
|
||||
'esnext.string.match-all',
|
||||
],
|
||||
}),
|
||||
vueJsx(),
|
||||
compression(),
|
||||
viteBuildInfo(),
|
||||
cdn,
|
||||
viteMockServe({ mockPath: 'src/mock' }),
|
||||
];
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import { type ServerOptions } from 'vite';
|
||||
|
||||
export const serverOptions = () => {
|
||||
const options: ServerOptions = {
|
||||
port: 6261,
|
||||
host: '0.0.0.0',
|
||||
open: true,
|
||||
cors: true,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: process.env.BUNNY_APP_URL,
|
||||
changeOrigin: true,
|
||||
rewrite: (path: string) => path.replace(/^\/api/, '/api'),
|
||||
},
|
||||
'/mock': {
|
||||
target: process.env.BUNNY_APP_URL,
|
||||
changeOrigin: true,
|
||||
rewrite: (path: string) => path.replace(/^\/mock/, '/mock'),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return options;
|
||||
};
|
|
@ -0,0 +1,33 @@
|
|||
import { readdir, stat } from 'node:fs';
|
||||
import { formatBytes, sum } from '@pureadmin/utils';
|
||||
|
||||
const fileListTotal: number[] = [];
|
||||
|
||||
/** 获取指定文件夹中所有文件的总大小 */
|
||||
const getPackageSize = options => {
|
||||
const { folder = 'dist', callback, format = true } = options;
|
||||
readdir(folder, (err, files: string[]) => {
|
||||
if (err) throw err;
|
||||
let count = 0;
|
||||
const checkEnd = () => {
|
||||
++count == files.length && callback(format ? formatBytes(sum(fileListTotal)) : sum(fileListTotal));
|
||||
};
|
||||
files.forEach((item: string) => {
|
||||
stat(`${folder}/${item}`, async (err, stats) => {
|
||||
if (err) throw err;
|
||||
if (stats.isFile()) {
|
||||
fileListTotal.push(stats.size);
|
||||
checkEnd();
|
||||
} else if (stats.isDirectory()) {
|
||||
getPackageSize({
|
||||
folder: `${folder}/${item}/`,
|
||||
callback: checkEnd,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
files.length === 0 && callback(0);
|
||||
});
|
||||
};
|
||||
|
||||
export { getPackageSize };
|
File diff suppressed because it is too large
Load Diff
|
@ -20,15 +20,19 @@
|
|||
"commit": "git pull && git add -A && git-cz && git push"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pureadmin/utils": "^2.4.7",
|
||||
"@vitejs/plugin-legacy": "^5.4.0",
|
||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||
"@vitejs/plugin-vue2-jsx": "^1.1.1",
|
||||
"axios": "^1.6.7",
|
||||
"boxen": "^7.1.1",
|
||||
"compression-webpack-plugin": "^11.1.0",
|
||||
"core-js": "^3.36.0",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.10",
|
||||
"dotenv": "^16.4.5",
|
||||
"echarts": "^5.5.0",
|
||||
"gradient-string": "^2.0.2",
|
||||
"lodash": "^4.17.21",
|
||||
"mitt": "^3.0.1",
|
||||
"moment": "^2.30.1",
|
||||
|
|
124
vite.config.ts
124
vite.config.ts
|
@ -1,101 +1,29 @@
|
|||
import vue from "@vitejs/plugin-vue";
|
||||
import { resolve } from "path";
|
||||
import { defineConfig, UserConfig } from "vite";
|
||||
import legacy from "@vitejs/plugin-legacy";
|
||||
import vueJsx from "@vitejs/plugin-vue2-jsx";
|
||||
import { compression } from "vite-plugin-compression2";
|
||||
import cdn from "vite-plugin-cdn-import";
|
||||
import { viteMockServe } from "vite-plugin-mock";
|
||||
import { resolve } from 'path';
|
||||
import { defineConfig, UserConfig } from 'vite';
|
||||
import { buildEnvironment } from './build/buildEnv';
|
||||
import { exclude, include } from './build/optimize';
|
||||
import { getPluginsList } from './build/plugins';
|
||||
import { serverOptions } from './build/server';
|
||||
|
||||
export default defineConfig(
|
||||
(): UserConfig => ({
|
||||
// base: './', // ? 在每个文件前加上这个前缀
|
||||
// publicDir: './static',// ? 设置静态资源目录
|
||||
envPrefix: "BUNNY",
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": resolve(__dirname, "./src"),
|
||||
"vue-i18n": "vue-i18n/dist/vue-i18n.cjs.js"
|
||||
}
|
||||
},
|
||||
server: {
|
||||
host: "0.0.0.0",
|
||||
port: 6261,
|
||||
open: true,
|
||||
cors: true,
|
||||
proxy: {
|
||||
"/api": {
|
||||
target: process.env.BUNNY_APP_URL,
|
||||
changeOrigin: true,
|
||||
rewrite: (path: string) => path.replace(/^\/api/, "/api")
|
||||
},
|
||||
"/mock": {
|
||||
target: process.env.BUNNY_APP_URL,
|
||||
changeOrigin: true,
|
||||
rewrite: (path: string) => path.replace(/^\/mock/, "/mock")
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
vue(),
|
||||
legacy({ targets: ["defaults", "not IE 11"] }),
|
||||
vueJsx(),
|
||||
compression(),
|
||||
cdn({
|
||||
modules: []
|
||||
}),
|
||||
viteMockServe({ mockPath: "src/mock" })
|
||||
],
|
||||
esbuild: {
|
||||
pure: ["console.log", "debugger"],
|
||||
jsxFactory: "h",
|
||||
jsxFragment: "Fragment",
|
||||
jsxInject: "import { h } from 'vue';"
|
||||
},
|
||||
// 配置构建过程的选项,例如是否生成压缩文件和源映射
|
||||
build: {
|
||||
assetsInlineLimit: 20000,
|
||||
// 构建输出的目录,默认值为"dist"
|
||||
outDir: "dist",
|
||||
// 用于指定使用的代码压缩工具。在这里,minify 被设置为 'terser',表示使用 Terser 进行代码压缩。默认值terser
|
||||
// esbuild 打包更快,但是不能去除 console.log,terser打包慢,但能去除 console.log
|
||||
minify: "terser",
|
||||
// 用于配置 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,
|
||||
// 用于配置 Rollup 打包工具的选项
|
||||
rollupOptions: {
|
||||
// 用于配置输出选项
|
||||
output: {
|
||||
// 静态资源分类和包装
|
||||
chunkFileNames: "js/[name]-[hash].js", // 用于指定代码分块的输出文件名格式
|
||||
entryFileNames: "js/[name]-[hash].js", // 用于指定入口文件的输出文件名格式
|
||||
assetFileNames: "assets/[ext]/[name]-[hash].[ext]", // 用于指定静态资源的输出文件名格式
|
||||
// ? 配置文件生成方式
|
||||
manualChunks: (id, meta) => {
|
||||
// 如果是包含在包中则打包成 vendor
|
||||
if (id.includes("node_modules")) {
|
||||
return "vendor";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
(): UserConfig => ({
|
||||
envPrefix: 'BUNNY',
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, './src'),
|
||||
'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js',
|
||||
},
|
||||
},
|
||||
optimizeDeps: { include, exclude },
|
||||
server: serverOptions(),
|
||||
plugins: getPluginsList(),
|
||||
esbuild: {
|
||||
pure: ['console.log', 'debugger'],
|
||||
jsxFactory: 'h',
|
||||
jsxFragment: 'Fragment',
|
||||
jsxInject: "import { h } from 'vue';",
|
||||
},
|
||||
// 配置构建过程的选项,例如是否生成压缩文件和源映射
|
||||
build: buildEnvironment(),
|
||||
}),
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue