如何将三个相似Vue组件合并为单一可复用组件?遵循DRY原则优化
当然可以合并成一个可复用组件!
这正是DRY(Don't Repeat Yourself)原则的典型应用场景——我们只需要把三个组件的**公共部分(结构、基础样式)提取出来,将差异部分(颜色、计算逻辑)**做成可配置项,就能实现高度复用。下面给你两种最优方案,根据你的场景选择:
方案一:Props + 内部封装固定逻辑(适合逻辑稳定的场景)
如果这三种计算逻辑是固定需求,不会频繁新增,推荐这种方案:组件内部封装好三种计算逻辑,通过Props传递颜色、计算类型和对应参数,父组件使用时只需简单配置。
复用组件代码
<template> <div class="square-box" :style="{ backgroundColor: color }"> {{ computedValue }} </div> </template> <script setup> import { computed } from 'vue'; const props = defineProps({ // 方形背景色(必填) color: { type: String, required: true }, // 计算类型:限定为三种固定逻辑 computeType: { type: String, required: true, validator: (val) => ['percentage', 'ceil', 'average'].includes(val) }, // 计算所需的参数数组,根据类型传入对应数量的值 values: { type: Array, required: true } }); // 计算最终显示的值 const computedValue = computed(() => { switch (props.computeType) { case 'percentage': // 红色组件逻辑:两个值计算百分比 if (props.values.length !== 2) throw new Error('百分比计算需要传入2个值'); const [num, total] = props.values; return `${((num / total) * 100).toFixed(1)}%`; case 'ceil': // 蓝色组件逻辑:浮点值向上取整 if (props.values.length !== 1) throw new Error('向上取整需要传入1个值'); return Math.ceil(props.values[0]).toString(); case 'average': // 绿色组件逻辑:三个值取平均值 if (props.values.length !== 3) throw new Error('平均值计算需要传入3个值'); const sum = props.values.reduce((acc, cur) => acc + cur, 0); return (sum / 3).toFixed(1); default: return ''; } }); </script> <style scoped> .square-box { width: 100px; height: 100px; display: flex; align-items: center; justify-content: center; color: #fff; /* 保证文字与背景对比清晰 */ border-radius: 4px; /* 可选:增加一点圆角优化视觉 */ } </style>
父组件使用示例
<!-- 原红色百分比组件 --> <SquareBox color="#ff4444" computeType="percentage" :values="[25, 100]" /> <!-- 原蓝色向上取整组件 --> <SquareBox color="#33b5e5" computeType="ceil" :values="[3.1415]" /> <!-- 原绿色平均值组件 --> <SquareBox color="#99cc00" computeType="average" :values="[10, 20, 30]" />
方案二:Props + 自定义计算函数(适合灵活扩展的场景)
如果未来可能需要新增更多不同的计算逻辑,或者希望把计算逻辑交给父组件控制,推荐这种方案:组件只负责渲染方形容器,计算逻辑通过函数Props传入,完全解耦渲染和业务逻辑。
复用组件代码
<template> <div class="square-box" :style="{ backgroundColor: color }"> {{ computedValue }} </div> </template> <script setup> import { computed } from 'vue'; const props = defineProps({ color: { type: String, required: true }, // 父组件传入的计算函数 computeFn: { type: Function, required: true }, // 计算函数所需的参数数组 computeParams: { type: Array, default: () => [] } }); // 调用父组件传入的函数计算值 const computedValue = computed(() => { return props.computeFn(...props.computeParams); }); </script> <style scoped> .square-box { width: 100px; height: 100px; display: flex; align-items: center; justify-content: center; color: #fff; border-radius: 4px; } </style>
父组件使用示例
<!-- 原红色百分比组件 --> <SquareBox color="#ff4444" :computeFn="(num, total) => `${((num/total)*100).toFixed(1)}%`" :computeParams="[25, 100]" /> <!-- 原蓝色向上取整组件 --> <SquareBox color="#33b5e5" :computeFn="(val) => Math.ceil(val).toString()" :computeParams="[3.1415]" /> <!-- 原绿色平均值组件 --> <SquareBox color="#99cc00" :computeFn="(a,b,c) => ((a+b+c)/3).toFixed(1)" :computeParams="[10,20,30]" />
方案选择建议
- 如果你的三种计算逻辑是长期固定的,不会轻易新增,选方案一:逻辑内聚在组件内部,父组件使用更简洁,维护成本更低。
- 如果未来可能要添加更多自定义计算逻辑,或者需要让组件适配更多场景,选方案二:完全解耦渲染和业务逻辑,灵活性拉满。
内容的提问来源于stack exchange,提问作者LeBlaireau
相关产品推荐
相关产品推荐

