perf: 首屏优化,减少 1.5MB 资源

This commit is contained in:
xiaoxian521 2022-11-29 19:30:33 +08:00
parent a22bc8622e
commit d2b1bd5b44
17 changed files with 1842 additions and 4612 deletions

View File

@ -32,13 +32,14 @@ export const include = [
"lodash-unified",
"@ctrl/tinycolor",
"china-area-data",
"@faker-js/faker",
"vue-json-pretty",
"@logicflow/core",
"@pureadmin/utils",
"@wangeditor/editor",
"responsive-storage",
"@howdyjs/mouse-menu",
"@logicflow/extension",
"vue-virtual-scroller",
"element-resize-detector",
"@amap/amap-jsapi-loader",
"el-table-infinite-scroll",

View File

@ -86,7 +86,6 @@
"devDependencies": {
"@commitlint/cli": "13.1.0",
"@commitlint/config-conventional": "13.1.0",
"@faker-js/faker": "^7.5.0",
"@iconify-icons/carbon": "^1.2.8",
"@iconify-icons/ep": "^1.2.7",
"@iconify-icons/fa": "^1.2.3",

6199
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,12 @@
import iconifyIconOffline from "./src/iconifyIconOffline";
import iconifyIconOnline from "./src/iconifyIconOnline";
import iconSelect from "./src/Select.vue";
import fontIcon from "./src/iconfont";
/** 本地图标组件 */
const IconifyIconOffline = iconifyIconOffline;
/** 在线图标组件 */
const IconifyIconOnline = iconifyIconOnline;
/** 图标选择器组件 */
const IconSelect = iconSelect;
/** iconfont组件 */
const FontIcon = fontIcon;
export { IconifyIconOffline, IconifyIconOnline, IconSelect, FontIcon };
export { IconifyIconOffline, IconifyIconOnline, FontIcon };

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
import { cloneDeep } from "lodash-unified";
import { ref, computed, CSSProperties, toRef, watch } from "vue";
import { IconJson } from "@/components/ReIcon/data";
import { ref, computed, CSSProperties, toRef, watch } from "vue";
type ParameterCSSProperties = (item?: string) => CSSProperties | undefined;
defineOptions({

View File

@ -42,7 +42,6 @@ const verticalRef = ref();
const horizontalRef = ref();
const {
body,
dataTheme,
layoutTheme,
themeColors,
@ -165,8 +164,6 @@ function setFalse(Doms): any {
}
watch($storage, ({ layout }) => {
/* 设置wangeditorV5主题色 */
body.style.setProperty("--w-e-toolbar-active-color", layout["epThemeColor"]);
switch (layout["layout"]) {
case "vertical":
toggleClass(true, isSelect, unref(verticalRef));

View File

@ -8,7 +8,6 @@ import { useGlobal } from "@pureadmin/utils";
import { transformI18n } from "@/plugins/i18n";
import { router, remainingPaths } from "@/router";
import { useAppStoreHook } from "@/store/modules/app";
import { i18nChangeLanguage } from "@wangeditor/editor";
import { useUserStoreHook } from "@/store/modules/user";
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
import { usePermissionStoreHook } from "@/store/modules/permission";
@ -136,15 +135,6 @@ export function useNav() {
return remainingPaths.includes(path);
}
/**
* wangEditorV5国际化
* @param language string enzh-CN
* @returns void
*/
function changeWangeditorLanguage(language: string): void {
i18nChangeLanguage(language);
}
return {
title,
device,
@ -164,7 +154,6 @@ export function useNav() {
username,
avatarsStyle,
getDropdownItemStyle,
getDropdownItemClass,
changeWangeditorLanguage
getDropdownItemClass
};
}

View File

@ -4,8 +4,7 @@ import { useRoute } from "vue-router";
import { watch, type Ref } from "vue";
export function useTranslationLang(ref?: Ref) {
const { $storage, changeTitle, changeWangeditorLanguage, handleResize } =
useNav();
const { $storage, changeTitle, handleResize } = useNav();
const { locale, t } = useI18n();
const route = useRoute();
@ -25,9 +24,6 @@ export function useTranslationLang(ref?: Ref) {
() => locale.value,
() => {
changeTitle(route.meta);
locale.value === "en"
? changeWangeditorLanguage(locale.value)
: changeWangeditorLanguage("zh-CN");
}
);

View File

@ -1,4 +1,5 @@
<script setup lang="ts">
import "animate.css";
import { setType } from "./types";
import { emitter } from "@/utils/mitt";
import { useLayout } from "./hooks/useLayout";

View File

@ -8,13 +8,11 @@ import { createApp, Directive } from "vue";
import { MotionPlugin } from "@vueuse/motion";
import { useEcharts } from "@/plugins/echarts";
import { useTable } from "@/plugins/vxe-table";
import VirtualScroller from "vue-virtual-scroller";
import { injectResponsiveStorage } from "@/utils/responsive";
import Table from "@pureadmin/table";
import PureDescriptions from "@pureadmin/descriptions";
import "animate.css";
// 引入重置样式
import "./style/reset.scss";
// 导入公共样式
@ -23,8 +21,6 @@ import "element-plus/dist/index.css";
// 导入字体图标
import "./assets/iconfont/iconfont.js";
import "./assets/iconfont/iconfont.css";
import "v-contextmenu/dist/themes/default.css";
import "vue-virtual-scroller/dist/vue-virtual-scroller.css";
const app = createApp(App);
@ -60,7 +56,6 @@ getServerConfig(app).then(async config => {
.use(Table)
.use(PureDescriptions)
.use(useTable)
.use(useEcharts)
.use(VirtualScroller);
.use(useEcharts);
app.mount("#app");
});

View File

@ -1,6 +1,6 @@
<script setup lang="ts">
import { ref } from "vue";
import { IconSelect } from "@/components/ReIcon";
import IconSelect from "@/components/ReIcon/src/Select.vue";
defineOptions({
name: "IconSelect"

View File

@ -1,70 +0,0 @@
import { faker } from "@faker-js/faker";
let uid = 0;
function generateItem() {
return {
name: faker.name.findName(),
avatar: faker.internet.avatar()
};
}
export function getData(count, letters) {
const raw = {};
const alphabet = "abcdefghijklmnopqrstuvwxyz".split("");
for (const l of alphabet) {
raw[l] = [];
}
for (let i = 0; i < count; i++) {
const item = generateItem();
const letter = item.name.charAt(0).toLowerCase();
raw[letter].push(item);
}
const list = [];
let index = 1;
for (const l of alphabet) {
raw[l] = raw[l].sort((a, b) => (a.name < b.name ? -1 : 1));
if (letters) {
list.push({
id: uid++,
index: index++,
type: "letter",
value: l,
height: 200
});
}
for (const item of raw[l]) {
list.push({
id: uid++,
index: index++,
type: "person",
value: item,
height: 50
});
}
}
return list;
}
export function addItem(list) {
list.push({
id: uid++,
index: list.length + 1,
type: "person",
value: generateItem(),
height: 50
});
}
export function generateMessage() {
return {
avatar: faker.internet.avatar(),
message: faker.lorem.text()
};
}

View File

@ -1,33 +1,27 @@
<script setup lang="ts">
import { ref, computed } from "vue";
import { generateMessage } from "./data";
import { DynamicScroller, DynamicScrollerItem } from "vue-virtual-scroller";
const items = ref([]);
const search = ref("");
for (let i = 0; i < 10000; i++) {
for (let i = 0; i < 800; i++) {
items.value.push({
id: i,
...generateMessage()
id: i
});
}
const filteredItems = computed(() => {
if (!search.value) return items.value;
const lowerCaseSearch = search.value.toLowerCase();
return items.value.filter(i =>
i.message.toLowerCase().includes(lowerCaseSearch)
);
const lowerCaseSearch = search.value;
return items.value.filter(i => i.id == lowerCaseSearch);
});
function changeMessage(message) {
Object.assign(message, generateMessage());
}
</script>
<template>
<div class="dynamic-scroller-demo">
<div class="flex justify-around mb-4">
<div class="flex-ac mb-4 shadow-2xl">
水平模式 horizontal
<el-input
class="mr-2 !w-[1/1.5]"
clearable
@ -35,7 +29,6 @@ function changeMessage(message) {
placeholder="Filter..."
style="width: 300px"
/>
<el-tag effect="dark">水平模式horizontal</el-tag>
</div>
<DynamicScroller
@ -48,31 +41,21 @@ function changeMessage(message) {
<DynamicScrollerItem
:item="item"
:active="active"
:size-dependencies="[item.message]"
:size-dependencies="[item.id]"
:data-index="index"
:data-active="active"
:title="`Click to change message ${index}`"
:style="{
width: `${Math.max(
130,
Math.round((item.message.length / 20) * 20)
)}px`
width: `${Math.max(130, Math.round((item.id?.length / 20) * 20))}px`
}"
class="message"
@click="changeMessage(item)"
>
<div class="avatar">
<div>
<IconifyIconOnline
icon="openmoji:beaming-face-with-smiling-eyes"
width="40"
/>
</div>
<div class="text">
{{ item.message }}
</div>
<div class="index">
<span>{{ item.id }} (id)</span>
<span>{{ index }} (index)</span>
<p class="text-center">{{ item.id }}</p>
</div>
</DynamicScrollerItem>
</template>
@ -104,35 +87,4 @@ function changeMessage(message) {
padding: 12px;
box-sizing: border-box;
}
.avatar {
flex: auto 0 0;
width: 32px;
height: 32px;
border-radius: 50%;
margin-bottom: 12px;
}
.avatar .image {
max-width: 100%;
max-height: 100%;
border-radius: 50%;
}
.index,
.text {
flex: 1;
}
.text {
margin-bottom: 12px;
}
.index {
opacity: 0.5;
}
.index span {
display: block;
}
</style>

View File

@ -1,6 +1,7 @@
<script setup lang="ts">
import verticalList from "./vertical.vue";
import horizontalList from "./horizontal.vue";
import "vue-virtual-scroller/dist/vue-virtual-scroller.css";
defineOptions({
name: "VirtualList"

View File

@ -1,75 +1,56 @@
<script setup lang="ts">
import { ref, computed } from "vue";
import { generateMessage } from "./data";
import { DynamicScroller, DynamicScrollerItem } from "vue-virtual-scroller";
const items = ref([]);
const search = ref("");
for (let i = 0; i < 10000; i++) {
for (let i = 0; i < 800; i++) {
items.value.push({
id: i,
...generateMessage()
id: i
});
}
const filteredItems = computed(() => {
if (!search.value) return items.value;
const lowerCaseSearch = search.value.toLowerCase();
return items.value.filter(i =>
i.message.toLowerCase().includes(lowerCaseSearch)
);
const lowerCaseSearch = search.value;
return items.value.filter(i => i.id == lowerCaseSearch);
});
function changeMessage(message) {
Object.assign(message, generateMessage());
}
function onResize() {
console.log("resize");
}
</script>
<template>
<div class="dynamic-scroller-demo">
<div class="flex justify-around mb-4">
<div class="flex-ac mb-4 shadow-2xl">
垂直模式 vertical
<el-input
class="mr-2 !w-[1/1.5]"
class="!w-[350px]"
clearable
v-model="search"
placeholder="Filter..."
/>
<el-tag effect="dark">垂直模式vertical</el-tag>
</div>
<DynamicScroller
:items="filteredItems"
:min-item-size="54"
class="scroller"
@resize="onResize"
>
<template #default="{ item, index, active }">
<DynamicScrollerItem
:item="item"
:active="active"
:size-dependencies="[item.message]"
:size-dependencies="[item.id]"
:data-index="index"
:data-active="active"
:title="`Click to change message ${index}`"
class="message"
@click="changeMessage(item)"
>
<div class="avatar">
<div class="flex items-center">
<IconifyIconOnline
icon="openmoji:beaming-face-with-smiling-eyes"
width="40"
/>
</div>
<div class="text">
{{ item.message }}
</div>
<div class="index">
<span>{{ item.id }} (id)</span>
<span>{{ index }} (index)</span>
<span>{{ item.id }}</span>
</div>
</DynamicScrollerItem>
</template>
@ -94,31 +75,4 @@ function onResize() {
padding: 12px;
box-sizing: border-box;
}
.avatar {
flex: auto 0 0;
width: 32px;
height: 32px;
border-radius: 50%;
margin-right: 12px;
}
.index,
.text {
flex: 1;
}
.text {
max-width: 400px;
}
.index {
opacity: 0.5;
}
.index span {
display: inline-block;
width: 160px;
text-align: right;
}
</style>

View File

@ -2,6 +2,7 @@
import basic from "./basic.vue";
import menuGroup from "./menuGroup.vue";
import menuDynamic from "./menuDynamic.vue";
import "v-contextmenu/dist/themes/default.css";
defineOptions({
name: "ContextMenu"

View File

@ -195,7 +195,7 @@ onUnmounted(() => {
>
<TypeIt
:className="'type-it2'"
:values="['Pure-Admin 版本日志']"
:values="['PureAdmin 版本日志']"
:cursor="false"
:speed="80"
/>