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
相关产品推荐
相关产品推荐

