前端开发 Vue 组件优化

1. 按需导入与代码分割

对于一些大型库,尽可能只导入需要的部分:

javascript 复制代码
// 好的做法
import { Button, Select } from 'element-plus'

// 避免这样做
// import ElementPlus from 'element-plus'

代码分割是一种将应用分解成 分成较小的块 的技术,可以显著提高加载性能。在 Vue 中,我们可以结合动态导入和异步组件来实现:

配置路由:

javascript 复制代码
// router.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from './views/Home.vue'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // 路由级代码分割
    component: () => import('./views/About.vue')
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router

页面引入:

html 复制代码
// App.vue
<template>
  <div>
    <!-- 其他内容 -->
    <router-view></router-view>
  </div>
</template>

<script>
import { defineAsyncComponent } from 'vue'

// 组件级代码分割
const AsyncComponent = defineAsyncComponent(() =>
  import('./components/HeavyComponent.vue')
)

export default {
  components: {
    AsyncComponent
  }
}
</script>

2. 确保 Props 稳定性

为了避免不必要的子组件更新,我们应该尽量保持传递给子组件的 props 稳定。这里有一个优化例子:

html 复制代码
<!-- 优化前 -->
<template>
  <ul>
    <ListItem
      v-for="item in items"
      :key="item.id"
      :item="item"
      :is-selected="selectedId === item.id"
      @select="selectItem"
    />
  </ul>
</template>

<script>
export default {
  data() {
    return {
      items: [/* ... */],
      selectedId: null
    }
  },
  methods: {
    selectItem(id) {
      this.selectedId = id
    }
  }
}
</script>

以下是代码优化后:

html 复制代码
<!-- 优化后 -->
<template>
  <ul>
    <ListItem
      v-for="item in itemsWithSelection"
      :key="item.id"
      :item="item"
      @select="selectItem"
    />
  </ul>
</template>

<script>
export default {
  data() {
    return {
      items: [/* ... */],
      selectedId: null
    }
  },
  computed: {
    itemsWithSelection() {
      return this.items.map(item => ({
        ...item,
        isSelected: item.id === this.selectedId
      }))
    }
  },
  methods: {
    selectItem(id) {
      this.selectedId = id
    }
  }
}
</script>

在优化后的版本中,我们将选择状态的计算移到了父组件的计算属性中。这样可以减少子组件的不必要更新,因为现在传递给子组件的 props 更加稳定。

3. v-once 和 v-memo 的使用

v-once 指令用于指定只需要渲染一次的内容。这对于静态内容特别有用:

html 复制代码
<template>
  <!-- 只渲染一次 -->
  <h1 v-once>{{ title }}</h1>
</template>

v-memo 指令用于有条件地跳过组件或元素的更新,可以理解为只在给定状态数据变更时,更新视图:

html 复制代码
<template>
  <!-- 只在 `name` 改变时才更新 -->
  <div v-memo="[name]">
    <!-- 复杂的子树 -->
  </div>
</template>

4. 大型虚拟列表

对于渲染大量数据的列表,可以使用虚拟列表技术。以下是使用 vue-virtual-scroller 的例子:

html 复制代码
<template>
  <RecycleScroller
    class="scroller"
    :items="list"
    :item-size="32"
    key-field="id"
    v-slot="{ item }"
  >
    <div class="user">
      {{ item.name }}
    </div>
  </RecycleScroller>
</template>

<script>
import { RecycleScroller } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'

export default {
  components: {
    RecycleScroller
  },
  data() {
    return {
      list: Array.from({ length: 10000 }, (_, i) => ({
        id: i,
        name: `User ${i}`
      }))
    }
  }
}
</script>

<style>
.scroller {
  height: 300px;
}
.user {
  height: 32px;
  padding: 0 12px;
  display: flex;
  align-items: center;
}
</style>

或者使用 VueUse 中的 useVirtualList

如果在真正大表格大数据量的情况下,我们需要考虑使用 Canvas table 方案。

5. 减少大型不可变数据的响应性开销

对于大型的、不经常变化的数据,可以使用 shallowRef 或 shallowReactive 来减少响应性开销:

javascript 复制代码
import { shallowRef } from 'vue'

export default {
  setup() {
    const largeData = shallowRef([
      // 大量数据...
    ])

    const updateData = () => {
      // 错误:不会触发更新
      // largeData.value.push(newItem)

      // 正确:替换整个引用以触发更新
      largeData.value = [...largeData.value, newItem]
    }

    return {
      largeData,
      updateData
    }
  }
}

使用 shallowRef 可以显著减少大型数据结构的响应性开销,但要注意,这意味着只有顶层属性的变化会触发更新,一定要确保视图的更新不依赖于下层数据。

6. 避免不必要的组件抽象

虽然组件抽象可以提高代码的可维护性,但过度的抽象可能导致性能问题。 特别是在渲染大列表时,应该谨慎使用小型的、功能单一的组件。

html 复制代码
<!-- 优化前:每个项都是一个单独的组件 -->
<template>
  <ul>
    <ListItem
      v-for="item in items"
      :key="item.id"
      :item="item"
    />
  </ul>
</template>

<!-- 优化后:整个列表作为一个组件 -->
<template>
  <ul>
    <li v-for="item in items" :key="item.id">
      {{ item.name }}
      <!-- 其他项目内容 -->
    </li>
  </ul>
</template>

<script>
export default {
  props: {
    items: Array
  }
}
</script>
相关推荐
YGY Webgis糕手之路4 小时前
OpenLayers 综合案例-轨迹回放
前端·经验分享·笔记·vue·web
程序员JerrySUN9 小时前
Linux系统架构核心全景详解
linux·运维·系统架构
YGY Webgis糕手之路9 小时前
OpenLayers 快速入门(九)Extent 介绍
前端·经验分享·笔记·vue·web
pengzhuofan11 小时前
Web开发系列-第13章 Vue3 + ElementPlus
前端·elementui·vue·web
蝸牛ちゃん12 小时前
设计模式(七)结构型:适配器模式详解
设计模式·系统架构·软考高级·适配器模式
蝸牛ちゃん13 小时前
设计模式(十二)结构型:享元模式详解
设计模式·系统架构·软考高级·享元模式
程序员编程指南14 小时前
Qt 远程过程调用(RPC)实现方案
c语言·c++·qt·rpc·系统架构
蝸牛ちゃん17 小时前
设计模式(十三)结构型:代理模式详解
设计模式·系统架构·代理模式·软考高级
weixin_307779132 天前
Windows 11下纯软件模拟虚拟机的设备模拟与虚拟化(仅终端和网络)
windows·系统架构