mirror of
https://gitee.com/durcframework/SOP.git
synced 2025-08-11 12:56:28 +08:00
5.1
This commit is contained in:
60
sop-website/sop-website-frontend/build/cdn.ts
Executable file
60
sop-website/sop-website-frontend/build/cdn.ts
Executable file
@@ -0,0 +1,60 @@
|
|||||||
|
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}",
|
||||||
|
modules: [
|
||||||
|
{
|
||||||
|
name: "vue",
|
||||||
|
var: "Vue",
|
||||||
|
path: "vue.global.prod.min.js"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "vue-router",
|
||||||
|
var: "VueRouter",
|
||||||
|
path: "vue-router.global.min.js"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "vue-i18n",
|
||||||
|
var: "VueI18n",
|
||||||
|
path: "vue-i18n.runtime.global.prod.min.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: "index.iife.min.js"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "pinia",
|
||||||
|
var: "Pinia",
|
||||||
|
path: "pinia.iife.min.js"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "element-plus",
|
||||||
|
var: "ElementPlus",
|
||||||
|
path: "index.full.min.js",
|
||||||
|
css: "index.min.css"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "axios",
|
||||||
|
var: "axios",
|
||||||
|
path: "axios.min.js"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dayjs",
|
||||||
|
var: "dayjs",
|
||||||
|
path: "dayjs.min.js"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "echarts",
|
||||||
|
var: "echarts",
|
||||||
|
path: "echarts.min.js"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
63
sop-website/sop-website-frontend/build/compress.ts
Executable file
63
sop-website/sop-website-frontend/build/compress.ts
Executable file
@@ -0,0 +1,63 @@
|
|||||||
|
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;
|
||||||
|
};
|
55
sop-website/sop-website-frontend/build/info.ts
Executable file
55
sop-website/sop-website-frontend/build/info.ts
Executable file
@@ -0,0 +1,55 @@
|
|||||||
|
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(`Hello SOP`);
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
34
sop-website/sop-website-frontend/build/optimize.ts
Executable file
34
sop-website/sop-website-frontend/build/optimize.ts
Executable file
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* 此文件作用于 `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",
|
||||||
|
"dayjs",
|
||||||
|
"axios",
|
||||||
|
"pinia",
|
||||||
|
"vue-i18n",
|
||||||
|
"vue-types",
|
||||||
|
"js-cookie",
|
||||||
|
"vue-tippy",
|
||||||
|
"pinyin-pro",
|
||||||
|
"sortablejs",
|
||||||
|
"@vueuse/core",
|
||||||
|
"@pureadmin/utils",
|
||||||
|
"responsive-storage"
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在预构建中强制排除的依赖项
|
||||||
|
* 温馨提示:所有以 `@iconify-icons/` 开头引入的的本地图标模块,都应该加入到下面的 `exclude` 里,因为平台推荐的使用方式是哪里需要哪里引入而且都是单个的引入,不需要预构建,直接让浏览器加载就好
|
||||||
|
*/
|
||||||
|
const exclude = [
|
||||||
|
"@iconify-icons/ep",
|
||||||
|
"@iconify-icons/ri",
|
||||||
|
"@pureadmin/theme/dist/browser-utils"
|
||||||
|
];
|
||||||
|
|
||||||
|
export { include, exclude };
|
76
sop-website/sop-website-frontend/build/plugins.ts
Executable file
76
sop-website/sop-website-frontend/build/plugins.ts
Executable file
@@ -0,0 +1,76 @@
|
|||||||
|
import { cdn } from "./cdn";
|
||||||
|
import vue from "@vitejs/plugin-vue";
|
||||||
|
import { pathResolve } from "./utils";
|
||||||
|
import { viteBuildInfo } from "./info";
|
||||||
|
import svgLoader from "vite-svg-loader";
|
||||||
|
import type { PluginOption } from "vite";
|
||||||
|
import checker from "vite-plugin-checker";
|
||||||
|
import vueJsx from "@vitejs/plugin-vue-jsx";
|
||||||
|
import Inspector from "vite-plugin-vue-inspector";
|
||||||
|
import { configCompressPlugin } from "./compress";
|
||||||
|
import removeNoMatch from "vite-plugin-router-warn";
|
||||||
|
import { visualizer } from "rollup-plugin-visualizer";
|
||||||
|
import removeConsole from "vite-plugin-remove-console";
|
||||||
|
import { themePreprocessorPlugin } from "@pureadmin/theme";
|
||||||
|
import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite";
|
||||||
|
import { genScssMultipleScopeVars } from "../src/layout/theme";
|
||||||
|
import { vitePluginFakeServer } from "vite-plugin-fake-server";
|
||||||
|
|
||||||
|
export function getPluginsList(
|
||||||
|
VITE_CDN: boolean,
|
||||||
|
VITE_COMPRESSION: ViteCompression
|
||||||
|
): PluginOption[] {
|
||||||
|
const lifecycle = process.env.npm_lifecycle_event;
|
||||||
|
return [
|
||||||
|
vue(),
|
||||||
|
// jsx、tsx语法支持
|
||||||
|
vueJsx(),
|
||||||
|
VueI18nPlugin({
|
||||||
|
jitCompilation: false,
|
||||||
|
include: [pathResolve("../locales/**")]
|
||||||
|
}),
|
||||||
|
checker({
|
||||||
|
typescript: true,
|
||||||
|
vueTsc: true,
|
||||||
|
eslint: {
|
||||||
|
lintCommand: `eslint ${pathResolve("../{src,mock,build}/**/*.{vue,js,ts,tsx}")}`,
|
||||||
|
useFlatConfig: true
|
||||||
|
},
|
||||||
|
terminal: false,
|
||||||
|
enableBuild: false
|
||||||
|
}),
|
||||||
|
// 按下Command(⌘)+Shift(⇧),然后点击页面元素会自动打开本地IDE并跳转到对应的代码位置
|
||||||
|
Inspector(),
|
||||||
|
viteBuildInfo(),
|
||||||
|
/**
|
||||||
|
* 开发环境下移除非必要的vue-router动态路由警告No match found for location with path
|
||||||
|
* 非必要具体看 https://github.com/vuejs/router/issues/521 和 https://github.com/vuejs/router/issues/359
|
||||||
|
* vite-plugin-router-warn只在开发环境下启用,只处理vue-router文件并且只在服务启动或重启时运行一次,性能消耗可忽略不计
|
||||||
|
*/
|
||||||
|
removeNoMatch(),
|
||||||
|
// mock支持
|
||||||
|
vitePluginFakeServer({
|
||||||
|
logger: false,
|
||||||
|
include: "mock",
|
||||||
|
infixName: false,
|
||||||
|
enableProd: true
|
||||||
|
}),
|
||||||
|
// 自定义主题
|
||||||
|
themePreprocessorPlugin({
|
||||||
|
scss: {
|
||||||
|
multipleScopeVars: genScssMultipleScopeVars(),
|
||||||
|
extract: true
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
// svg组件化支持
|
||||||
|
svgLoader(),
|
||||||
|
VITE_CDN ? cdn : null,
|
||||||
|
configCompressPlugin(VITE_COMPRESSION),
|
||||||
|
// 线上环境删除console
|
||||||
|
removeConsole({ external: ["src/assets/iconfont/iconfont.js"] }),
|
||||||
|
// 打包分析
|
||||||
|
lifecycle === "report"
|
||||||
|
? visualizer({ open: true, brotliSize: true, filename: "report.html" })
|
||||||
|
: (null as any)
|
||||||
|
];
|
||||||
|
}
|
110
sop-website/sop-website-frontend/build/utils.ts
Executable file
110
sop-website/sop-website-frontend/build/utils.ts
Executable file
@@ -0,0 +1,110 @@
|
|||||||
|
import dayjs from "dayjs";
|
||||||
|
import { readdir, stat } from "node:fs";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { dirname, resolve } from "node:path";
|
||||||
|
import { sum, formatBytes } from "@pureadmin/utils";
|
||||||
|
import {
|
||||||
|
name,
|
||||||
|
version,
|
||||||
|
engines,
|
||||||
|
dependencies,
|
||||||
|
devDependencies
|
||||||
|
} from "../package.json";
|
||||||
|
|
||||||
|
/** 启动`node`进程时所在工作目录的绝对路径 */
|
||||||
|
const root: string = process.cwd();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 根据可选的路径片段生成一个新的绝对路径
|
||||||
|
* @param dir 路径片段,默认`build`
|
||||||
|
* @param metaUrl 模块的完整`url`,如果在`build`目录外调用必传`import.meta.url`
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 设置别名 */
|
||||||
|
const alias: Record<string, string> = {
|
||||||
|
"@": pathResolve("../src"),
|
||||||
|
"@build": pathResolve()
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 平台的名称、版本、运行所需的`node`和`pnpm`版本、依赖、最后构建时间的类型提示 */
|
||||||
|
const __APP_INFO__ = {
|
||||||
|
pkg: { name, version, engines, dependencies, devDependencies },
|
||||||
|
lastBuildTime: dayjs(new Date()).format("YYYY-MM-DD HH:mm:ss")
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 处理环境变量 */
|
||||||
|
const wrapperEnv = (envConf: Recordable): ViteEnv => {
|
||||||
|
// 默认值
|
||||||
|
const ret: ViteEnv = {
|
||||||
|
VITE_PORT: 9124,
|
||||||
|
VITE_PUBLIC_PATH: "",
|
||||||
|
VITE_ROUTER_HISTORY: "",
|
||||||
|
VITE_CDN: false,
|
||||||
|
VITE_HIDE_HOME: "false",
|
||||||
|
VITE_COMPRESSION: "none"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const envName of Object.keys(envConf)) {
|
||||||
|
let realName = envConf[envName].replace(/\\n/g, "\n");
|
||||||
|
realName =
|
||||||
|
realName === "true" ? true : realName === "false" ? false : realName;
|
||||||
|
|
||||||
|
if (envName === "VITE_PORT") {
|
||||||
|
realName = Number(realName);
|
||||||
|
}
|
||||||
|
ret[envName] = realName;
|
||||||
|
if (typeof realName === "string") {
|
||||||
|
process.env[envName] = realName;
|
||||||
|
} else if (typeof realName === "object") {
|
||||||
|
process.env[envName] = JSON.stringify(realName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
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 { root, pathResolve, alias, __APP_INFO__, wrapperEnv, getPackageSize };
|
Reference in New Issue
Block a user