前端开发 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>
相关推荐
文火冰糖的硅基工坊1 小时前
[创业之路-551]:党、政府、经济、军队、文化、学术、社会七大领域的社会角色与职务层次结构(宏观-中观-微观)
系统架构·跨学科融合
i紸定i1 小时前
解决html-to-image在 ios 上dom里面的图片不显示出来
前端·ios·vue·html·html-to-image
文火冰糖的硅基工坊21 小时前
[激光原理与应用-286]:理论 - 波动光学 - 不同频段电磁波的特点与差异性
系统架构··跨学科融合·电磁波·
Tadas-Gao1 天前
Java设计模式全景解析:从演进历程到创新实践
java·开发语言·微服务·设计模式·云原生·架构·系统架构
gptplusplus1 天前
超越“调参”:从系统架构师视角,重构 AI 智能体的设计范式
人工智能·重构·系统架构
尚学教辅学习资料2 天前
Vue3从入门到精通: 4.5 数据持久化与同步策略深度解析
vue·数据持久化
IT毕设实战小研2 天前
Java毕业设计选题推荐 |基于SpringBoot的健身爱好线上互动与打卡社交平台系统 互动打卡小程序系统
java·开发语言·vue.js·spring boot·vue·毕业设计·课程设计
文火冰糖的硅基工坊3 天前
[激光原理与应用-267]:理论 - 几何光学 - 胶卷相机的组成和工作原理
数码相机·系统架构
第七种黄昏3 天前
大事件项目拆解:登录访问拦截实现详解
前端框架·vue·js
极造数字3 天前
深度剖析MES/MOM系统架构:功能模块与核心优势解析
大数据·人工智能·物联网·系统架构·制造