跳到主要内容

前端性能与开发规范指南

基于项目的性能优化经验与最佳实践,特制定以下前端开发规范。在日常开发、新建项目或组件时,请务必遵守,以保证系统加载速度和运行性能。

一、组件引入与按需加载规范

1. 全局组件注册

  • 强制要求:创建全局组件必须使用 createAsyncComponent 进行异步注册,避免阻塞全局渲染。

2. Form 组件内的子组件加载

  • Form 组件会被大量页面引用,其内部注册的自定义子组件(如 src/components/Form/src/componentMap.ts 中的组件)也必须使用按需加载。

3. 大型第三方依赖处理

  • 对于第三方大型依赖(如 tinymcecodemirror 等),如果在基础打包模式中被排除,而在特定模式下需要动态使用,则必须在对应的入口处显式引入。
  • 经验总结:部分包的 CJS 模式不支持按需加载,若项目外排除了该依赖,需显式引用。

4. 首页顶部公共区域组件

  • 首页顶部公共区域的内容(例如顶部消息通知等),若组件内引用了较多依赖和内容,需使用 defineAsyncComponent 进行按需加载。
  • 示例:仪表盘顶部 WebSocket 通信组件 JDragNotice,可按需挂载并异步加载:
<template>
<JDragNotice v-if="dragNoticeShow" />
</template>
<script lang="ts" setup>
import { ref, defineAsyncComponent } from 'vue';

const dragNoticeShow = ref(false);
const JDragNotice = defineAsyncComponent(
() => import('/@/components/JDragNotice/JDragNotice.vue')
);
</script>

5. 入口页面仅加载当前所需内容(登录页、main.ts 等)

1)只在加载显示内容,组件需异步

❌ 错误:同步导入导致首屏加载全部组件

<template>
<div v-show="type === 'forgot'" :class="`${prefixCls}-form`">
<MiniForgotpad ref="forgotRef" @go-back="goBack" @success="handleSuccess" />
</div>
<div v-show="type === 'register'" :class="`${prefixCls}-form`">
<MiniRegister ref="registerRef" @go-back="goBack" @success="handleSuccess" />
</div>
<div v-show="type === 'codeLogin'" :class="`${prefixCls}-form`">
<MiniCodelogin ref="codeRef" @go-back="goBack" @success="handleSuccess" />
</div>
</template>
<script setup>
import MiniForgotpad from './MiniForgotpad.vue';
import MiniRegister from './MiniRegister.vue';
import MiniCodelogin from './MiniCodelogin.vue';
</script>

✅ 正确:按需异步加载

<script setup>
import { defineAsyncComponent } from 'vue';

const MiniForgotpad = defineAsyncComponent(() => import('./MiniForgotpad.vue'));
const MiniRegister = defineAsyncComponent(() => import('./MiniRegister.vue'));
const MiniCodelogin = defineAsyncComponent(() => import('./MiniCodelogin.vue'));
</script>
<template>
<div v-show="type === 'forgot'" :class="`${prefixCls}-form`">
<MiniForgotpad ref="forgotRef" @go-back="goBack" @success="handleSuccess" />
</div>
<div v-show="type === 'register'" :class="`${prefixCls}-form`">
<MiniRegister ref="registerRef" @go-back="goBack" @success="handleSuccess" />
</div>
<div v-show="type === 'codeLogin'" :class="`${prefixCls}-form`">
<MiniCodelogin ref="codeRef" @go-back="goBack" @success="handleSuccess" />
</div>
</template>

2)只导入最底层所需模块(避免聚合导出副作用)

从聚合入口(如 index.js)导入单个组件时,会间接加载该入口下的所有导出。应直接导入具体源文件。

问题示例components/Application/index.js 聚合了多个组件:

// components/Application/index.js

import { withInstall } from '/@/utils';
import appLogo from './src/AppLogo.vue';
import appProvider from './src/AppProvider.vue';
import appSearch from './src/search/AppSearch.vue';
import appLocalePicker from './src/AppLocalePicker.vue';
import appDarkModeToggle from './src/AppDarkModeToggle.vue';
export { useAppProviderContext } from './src/useAppContext';
export const AppLogo = withInstall(appLogo);
export const AppProvider = withInstall(appProvider);
export const AppSearch = withInstall(appSearch);
export const AppLocalePicker = withInstall(appLocalePicker);
export const AppDarkModeToggle = withInstall(appDarkModeToggle);

❌ 错误:仅需 AppProvider,但从聚合入口导入会连带加载 appLogoappSearchappLocalePickerappDarkModeToggle

import { AppProvider } from '/@/components/Application';

✅ 正确:直接导入目标组件源文件:

import AppProvider from '/@/components/Application/src/AppProvider.vue';

二、弹窗与交互性能规范

1. 复杂弹窗延迟挂载

  • 页面中引入内容较多的 Modal 弹窗,建议结合 v-ifsetTimeout 进行延迟挂载。避免在打开页面时就渲染大量隐藏的 DOM 元素,提升页面初始化速度。
<!-- 模板:使用 v-if 控制挂载 -->
<DisposeModal v-if="disposeModalShow" @register="registerDisposeModal" />
// 脚本:使用 setTimeout 延迟打开,避免首屏即挂载 (复杂弹窗延迟长点)
disposeModalShow.value = true;
setTimeout(() => {
openDisposeModal(true, { modalTitle: '查看详情', id: record.id });
}, 0);

三、路由与动态组件映射规范(Super 模块)

1. 排除无关的组件映射

  • super 模块下新建项目时,务必在 dynamicPages.tsimport.meta.glob 规则中排除(如 !../views/...)不需要全量映射的组件子目录,只保留入口。可显著降低首屏加载时的映射解析负担。

2. 独立项目自建映射

  • super 下新建的项目若较为独立,应自行建立该项目的动态组件映射关系,并确保按需加载。

3. 严禁滥用 register.ts 命名

  • src/views/super/registerSuper.ts 会全量加载 super 目录下所有的 register.ts 文件。
  • 强制要求register.ts 仅限用于注册路由,不应包含其他逻辑代码。
  • 禁止register.ts 中引入页面样式或额外业务组件,防止进入系统即被全量加载。

四、图标使用规范

1. 强制原生 Iconify 写法

  • 强制要求:使用 Iconify 图标时,禁止继续使用旧版 <Icon /> 包装组件,须统一使用 Iconify 原生按需加载写法。
  • 转换规则Iconify + 大驼峰图标集 + 大驼峰图标名(例如:<IconifyIonLanguage />)。Vite 已通过插件实现自动导入,无需在页面中手动 import

使用方式: 从 Iconify 获取图标名(格式:图标集:图标名),例如 ion:language,转换规则如下:

Iconify + IonLanguage<IconfiyIonLanguage />

Vite 中已通过 unplugin-vue-components/vite + unplugin-icons 自动导入,无需在页面中手动引入:

import IconfiyIonLanguage from '~icons/ion/language';

五、JVxeTable 使用规范

1. 禁止在页面内再次引入 JVxeTable

  • JVxeTable 已在全局注册,若在页面中再次 import 并局部使用,会覆盖全局注册的实例,导致内置的自定义组件(如单元格类型、扩展组件等)未正确注册,可能造成页面卡死或功能异常
  • 正确做法:仅在模板中直接使用 <JVxeTable>,无需在 <script> 中引入。

❌ 错误示例(请勿在页面内引入):

<template>
<JVxeTable
ref="tableRef1"
stripe
toolbar
rowNumber
rowSelection
resizable
keepSource
:height="tableH"
:checkbox-config="{ range: true }"
:loading="table1.loading"
:columns="table1.columns"
:dataSource="table1.dataSource"
/>
</template>
<script lang="ts" setup>
// ❌ 错误:页面内不要引入 JVxeTable
// import { JVxeTable } from '/@/components/jeecg/JVxeTable';
</script>

2. 使用 vxe-table 原生写法时需手动注册

  • 若采用 vxe-table 原生 API 的用法(非封装后的 <JVxeTable> 组件),需先通过 useVxeTableRegister 完成相关组件的注册,否则表格扩展能力无法生效。
  • 参考示例src/views/demo/vextable/index3.vue
import { useVxeTableRegister } from '/@/components/jeecg/JVxeTable/useVxeTableRegister';

六、UI 库及其他规范

1. Ant Design Vue 的使用

  • 框架通过 unplugin-vue-components/vite 实现了 template 中 antd 组件的按需动态加载。
  • 注意:项目中的 <a-button> 已被默认指向封装后的 BasicButton.vue。若特殊场景下必须使用 antd 原生 Button,须在业务代码中手动显式引入:
import { Button } from 'ant-design-vue';

2. Vite 启动预构建

  • 为提升本地开发环境的首屏访问速度,在 Vite 启动时应通过 warmup 配置预构建核心入口页面(如 main.tsApp.vue、登录页等)。