You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue.js组件优化:如何将4个同类组件合并为单一组件实现内容切换?

优化思路 & 实现示例

嘿,我完全懂你现在的困扰——四个几乎一模一样的组件,除了内容其他都相同,维护起来太麻烦了!咱们把它们合并成一个可复用组件其实很简单,核心就是把可变的部分抽离成配置,让组件根据传入的参数动态工作,具体步骤和示例如下:

核心思路

  • 第一步:先梳理出四个组件的差异点——无非就是「API地址」「显示标题」「按钮标识」这些,把这些整理成一个配置数组存在App.vue里。
  • 第二步:在App.vue里维护一个状态,用来记录当前用户点击的是哪个按钮对应的内容。
  • 第三步:创建一个通用的内容组件(比如叫ContentCard.vue),通过props接收当前选中的配置信息,然后根据配置调用对应的API、渲染对应的内容。
  • 第四步:点击App.vue里的按钮时,更新状态,把对应的配置传给通用组件即可。

代码示例

1. App.vue(父组件)

这里我们把四个按钮对应的配置统一管理,点击按钮时切换当前激活的配置:

<template>
  <div class="app-container">
    <!-- 按钮组 -->
    <div class="button-group">
      <button
        v-for="item in contentConfigs"
        :key="item.id"
        :class="{ active: currentConfig?.id === item.id }"
        @click="currentConfig = item"
      >
        {{ item.title }}
      </button>
    </div>

    <!-- 通用内容组件 -->
    <ContentCard :config="currentConfig" v-if="currentConfig" />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import ContentCard from './components/ContentCard.vue'

// 配置数组:把四个组件的差异都存在这里
const contentConfigs = ref([
  {
    id: 1,
    title: '内容1',
    apiUrl: '/api/content-1'
  },
  {
    id: 2,
    title: '内容2',
    apiUrl: '/api/content-2'
  },
  {
    id: 3,
    title: '内容3',
    apiUrl: '/api/content-3'
  },
  {
    id: 4,
    title: '内容4',
    apiUrl: '/api/content-4'
  }
])

// 当前选中的配置
const currentConfig = ref(contentConfigs.value[0]) // 默认选中第一个
</script>

<style scoped>
.button-group {
  margin-bottom: 20px;
}
button {
  padding: 8px 16px;
  margin-right: 10px;
  cursor: pointer;
}
button.active {
  background-color: #42b983;
  color: white;
  border: none;
  border-radius: 4px;
}
</style>

2. ContentCard.vue(通用子组件)

这个组件负责接收配置,调用对应API,渲染内容,所有重复的逻辑都在这里统一处理:

<template>
  <div class="content-card">
    <h2>{{ config.title }}</h2>
    <div v-if="loading" class="loading">加载中...</div>
    <div v-else-if="error" class="error">加载失败:{{ error }}</div>
    <div v-else class="content">
      <!-- 这里渲染API返回的内容,根据你的实际结构调整 -->
      <p>{{ contentData }}</p>
    </div>
  </div>
</template>

<script setup>
import { ref, watch, onMounted } from 'vue'

// 接收父组件传入的配置
const props = defineProps({
  config: {
    type: Object,
    required: true
  }
})

const contentData = ref('')
const loading = ref(false)
const error = ref('')

// 封装API调用函数
const fetchContent = async () => {
  loading.value = true
  error.value = ''
  try {
    const response = await fetch(props.config.apiUrl)
    if (!response.ok) throw new Error('请求失败')
    contentData.value = await response.json()
  } catch (err) {
    error.value = err.message
  } finally {
    loading.value = false
  }
}

// 组件挂载时调用一次,配置变化时重新调用
onMounted(fetchContent)
watch(() => props.config, fetchContent, { deep: true })
</script>

<style scoped>
.content-card {
  padding: 20px;
  border: 1px solid #eee;
  border-radius: 8px;
}
.loading {
  color: #666;
}
.error {
  color: #ff4d4f;
}
.content {
  margin-top: 15px;
  line-height: 1.6;
}
</style>

额外优化点(可选)

  • 如果你的API请求逻辑更复杂,可以把它抽成单独的composable(比如useContentFetch.js),让组件更干净。
  • 如果按钮和内容的对应关系还有其他差异(比如不同的渲染模板),可以在配置里加一个templateKey,然后在组件里用v-if或者动态组件来切换模板。

内容的提问来源于stack exchange,提问作者app_er

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:29:47