Skip to content

性能优化

本文档介绍前端性能优化的最佳实践。

代码优化

组件懒加载

typescript
// 路由懒加载
const routes = [
  {
    path: '/user',
    component: () => import('@/views/user/index.vue')
  }
];

// 组件懒加载
const UserList = defineAsyncComponent(() => 
  import('@/components/UserList.vue')
);

虚拟列表

对于大量数据的列表,使用虚拟滚动:

vue
<script setup lang="ts">
import { VirtualList } from '@vben/common-ui';

const data = ref(Array.from({ length: 10000 }, (_, i) => ({
  id: i,
  name: `Item ${i}`
})));
</script>

<template>
  <VirtualList :data="data" :item-height="50">
    <template #default="{ item }">
      <div>{{ item.name }}</div>
    </template>
  </VirtualList>
</template>

防抖和节流

typescript
import { debounce, throttle } from 'lodash-es';

// 防抖:延迟执行
const handleSearch = debounce((value: string) => {
  console.log('搜索:', value);
}, 300);

// 节流:限制执行频率
const handleScroll = throttle(() => {
  console.log('滚动');
}, 100);

资源优化

图片优化

vue
<template>
  <!-- 懒加载 -->
  <img v-lazy="imageUrl" alt="image" />
  
  <!-- WebP 格式 -->
  <picture>
    <source srcset="image.webp" type="image/webp" />
    <img src="image.jpg" alt="image" />
  </picture>
  
  <!-- 响应式图片 -->
  <img
    srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
    sizes="(max-width: 600px) 480px, (max-width: 900px) 800px, 1200px"
    src="medium.jpg"
    alt="image"
  />
</template>

代码分割

typescript
// vite.config.ts
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          'vue-vendor': ['vue', 'vue-router', 'pinia'],
          'ui-vendor': ['ant-design-vue'],
          'utils': ['lodash-es', 'dayjs']
        }
      }
    }
  }
});

渲染优化

v-show vs v-if

vue
<template>
  <!-- 频繁切换使用 v-show -->
  <div v-show="isVisible">内容</div>
  
  <!-- 条件很少改变使用 v-if -->
  <div v-if="isAdmin">管理员内容</div>
</template>

计算属性缓存

vue
<script setup lang="ts">
// 使用计算属性,自动缓存
const filteredList = computed(() => {
  return list.value.filter(item => item.status === 'active');
});

// 避免在模板中使用方法
// ❌ 不推荐
function getFilteredList() {
  return list.value.filter(item => item.status === 'active');
}
</script>

key 的使用

vue
<template>
  <!-- 使用唯一 key -->
  <div v-for="item in list" :key="item.id">
    {{ item.name }}
  </div>
  
  <!-- 避免使用 index 作为 key -->
  <!-- ❌ 不推荐 -->
  <div v-for="(item, index) in list" :key="index">
    {{ item.name }}
  </div>
</template>

网络优化

请求合并

typescript
// 使用 Promise.all 并发请求
async function fetchData() {
  const [users, roles, depts] = await Promise.all([
    getUserListApi(),
    getRoleListApi(),
    getDeptListApi()
  ]);
}

请求缓存

typescript
// 使用缓存避免重复请求
const cache = new Map();

async function getUserInfo(userId: string) {
  if (cache.has(userId)) {
    return cache.get(userId);
  }
  
  const result = await getUserInfoApi(userId);
  cache.set(userId, result);
  return result;
}

请求取消

typescript
import axios from 'axios';

const controller = new AbortController();

// 发起请求
axios.get('/api/data', {
  signal: controller.signal
});

// 取消请求
controller.abort();

构建优化

生产环境配置

typescript
// vite.config.ts
export default defineConfig({
  build: {
    // 压缩
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true
      }
    },
    // 分块
    chunkSizeWarningLimit: 1000,
    // 资源内联
    assetsInlineLimit: 4096
  }
});

Tree Shaking

typescript
// 使用 ES6 模块导入
import { debounce } from 'lodash-es';

// 避免导入整个库
// ❌ 不推荐
import _ from 'lodash';

监控和分析

性能监控

typescript
// 使用 Performance API
const start = performance.now();
// 执行操作
const end = performance.now();
console.log(`耗时: ${end - start}ms`);

打包分析

bash
# 分析打包体积
pnpm build:analyze

最佳实践

  1. 按需加载:路由和组件懒加载
  2. 代码分割:合理拆分代码块
  3. 资源优化:压缩图片、使用 WebP
  4. 缓存策略:合理使用浏览器缓存
  5. 减少重绘:避免频繁操作 DOM
  6. 虚拟滚动:大列表使用虚拟滚动
  7. 防抖节流:优化高频事件
  8. 预加载:关键资源预加载

参考资源

MIT License