欢迎光临
我们一直在努力

8、Vue3组件化实战:从0到1开发星级评分组件

Vue3组件化实战:从0到1开发星级评分组件

在Vue开发中,组件化是核心思想之一。通过组件化,我们可以像“搭积木”一样拆分、复用代码,让复杂页面的开发变得高效且可维护。

本文将以一个星级评分组件(Rate) 为例,从基础渲染到交互增强,一步步拆解Vue3组件化的设计思路和实现细节,帮你快速掌握自定义组件开发的核心要点。

一、组件化开发核心认知

在开始编码前,我们先理清组件化的核心概念:

  • 组件本质:把页面中可复用的功能模块(模板、逻辑、样式)封装成独立的.vue文件,实现“一次编写,多处复用”。
  • 组件分类:
    • 通用型组件:按钮、表单、弹窗等通用功能(如Element3组件库);
    • 业务型组件:和具体业务绑定的组件(如购物车、登录弹窗)。

评分组件属于通用型组件,核心需求是:支持1-5星的分数渲染、鼠标悬停预览、点击修改评分、自定义主题色,以及灵活的扩展能力。

二、基础实现:渲染星级评分

2.1 核心思路

评分组件的基础是根据传入的分数,渲染对应数量的实心星星。这里有一个极简的实现技巧:利用字符串的slice方法截取星星字符,一行代码就能实现分数对应渲染。

核心代码:

"★★★★★☆☆☆☆☆".slice(5 – rate, 10 – rate)

  • 当rate=3时,截取结果为★★★☆☆;
  • 当rate=4.5时(后续扩展小数支持),可通过宽度控制半星显示。

2.2 基础组件编写

在src/components目录下新建Rate.vue,实现基础的分数渲染:

<template>
<div>
{{ rate }}
</div>
</template>

<script setup>
import { defineProps, computed } from 'vue';

// 定义接收的属性:分数值(数字类型)
const props = defineProps({
value: Number
});

// 计算要显示的星星字符串
const rate = computed(() =>
"★★★★★☆☆☆☆☆".slice(5 – props.value, 10 – props.value)
);
</script>

2.3 组件使用

在父组件中引入并使用Rate组件,传递分数值:

<template>
<Rate :value="score"></Rate>
</template>

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

// 初始评分为3
const score = ref(3);
</script>

此时页面会显示★★★☆☆,基础的评分渲染就完成了。

三、进阶优化:主题定制+小数评分

3.1 自定义主题色

为组件添加theme属性,支持自定义星星颜色(默认橙色):

<template>
<div :style="fontStyle">
{{ rate }}
</div>
</template>

<script setup>
import { defineProps, computed } from 'vue';

const props = defineProps({
value: Number,
theme: {
type: String,
default: 'orange' // 默认主题色
}
});

// 主题色映射表
const themeMap = {
black: '#000',
white: '#fff',
red: '#f5222d',
orange: '#fa541c',
yellow: '#fadb14',
green: '#73d13d',
blue: '#40a9ff'
};

const rate = computed(() =>
"★★★★★☆☆☆☆☆".slice(5 – props.value, 10 – props.value)
);

// 计算字体样式(颜色)
const fontStyle = computed(() => ({
color: themeMap[props.theme] || themeMap.orange
}));
</script>

使用时指定主题色:

<Rate :value="3" theme="red"></Rate>
<Rate :value="4" theme="green"></Rate>

3.2 支持小数评分(鼠标悬停预览)

基础版只能渲染整数评分,我们通过“双层星星+宽度控制”实现小数/悬停预览效果:

<template>
<div :style="fontStyle">
<div class="rate" @mouseout="handleMouseOut">
<!– 底层空心星 –>
<span @mouseover="handleMouseOver(num)" v-for="num in 5" :key="num">☆</span>
<!– 上层实心星(通过宽度控制显示数量) –>
<span class="hollow" :style="fontWidth">
<span @mouseover="handleMouseOver(num)" v-for="num in 5" :key="num">★</span>
</span>
</div>
</div>
</template>

<script setup>
import { defineProps, computed, ref } from 'vue';

const props = defineProps({
value: Number,
theme: { type: String, default: 'orange' }
});

// 响应式宽度(控制实心星显示数量)
const width = ref(props.value);

// 鼠标悬停:预览对应分数
const handleMouseOver = (num) => {
width.value = num;
};

// 鼠标移出:恢复原始分数
const handleMouseOut = () => {
width.value = props.value;
};

// 主题色/宽度计算
const themeMap = { /* 同上 */ };
const fontStyle = computed(() => ({ color: themeMap[props.theme] || themeMap.orange }));
const fontWidth = computed(() => ({ width: `${width.value}em` }));
</script>

<style scoped>
.rate {
position: relative;
display: inline-block;
}
.rate > .hollow {
position: absolute;
top: 0;
left: 0;
width: 0;
overflow: hidden;
}
</style>

此时传入value=3.5,就能显示3.5星;鼠标悬停到第4星时,会预览4星效果。

四、交互增强:点击修改评分(emit通信)

组件需要把“用户点击的评分”传递给父组件,Vue3中通过defineEmits实现组件事件派发:

4.1 子组件派发事件

修改Rate.vue,添加点击事件并派发评分:

<template>
<div :style="fontStyle">
<div class="rate" @mouseout="handleMouseOut">
<span @mouseover="handleMouseOver(num)" v-for="num in 5" :key="num">☆</span>
<span class="hollow" :style="fontWidth">
<!– 新增点击事件 –>
<span
@click="handleRate(num)"
@mouseover="handleMouseOver(num)"
v-for="num in 5"
:key="num"
>★</span>
</span>
</div>
</div>
</template>

<script setup>
import { defineProps, defineEmits, computed, ref } from 'vue';

// 定义要派发的事件
const emit = defineEmits(['update-rate']);

// 点击评分:派发事件给父组件
const handleRate = (num) => {
emit('update-rate', num);
};

// 其余代码(props/theme/width等)不变
</script>

4.2 父组件接收事件

父组件监听update-rate事件,更新评分值:

<template>
<h1>你的评分:{{ score }}</h1>
<Rate :value="score" @update-rate="updateScore"></Rate>
</template>

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

const score = ref(3.5);

// 接收子组件的评分并更新
const updateScore = (num) => {
score.value = num;
};
</script>

五、简化用法:组件的v-model

Vue3支持自定义组件使用v-model,本质是props + emit的语法糖,无需手动写@update-rate,让代码更简洁。

5.1 子组件适配v-model

Vue3中自定义组件的v-model默认绑定modelValue属性,事件默认是update:modelValue:

<script setup>
// 1. 修改props名称为modelValue
const props = defineProps({
modelValue: Number, // 替换原来的value
theme: { type: String, default: 'orange' }
});

// 2. 修改事件名为update:modelValue
const emit = defineEmits(['update:modelValue']);

// 3. 点击事件派发update:modelValue
const handleRate = (num) => {
emit('update:modelValue', num);
};

// 4. 宽度初始值改为props.modelValue
const width = ref(props.modelValue);
const handleMouseOut = () => {
width.value = props.modelValue;
};

// 其余代码不变
</script>

5.2 父组件简化使用

直接用v-model绑定评分值,无需手动监听事件:

<template>
<h1>你的评分:{{ score }}</h1>
<!– 简化为v-model –>
<Rate v-model="score"></Rate>
</template>

六、灵活扩展:插槽(Slot)

像原生<button>可以嵌套内容一样,我们给Rate组件添加插槽,支持自定义前置内容(如文本、图标):

6.1 子组件添加插槽

修改Rate.vue的模板,加入<slot>:

<template>
<div :style="fontStyle">
<!– 插槽:自定义前置内容 –>
<slot></slot>
<div class="rate" @mouseout="handleMouseOut">
<!– 星星部分代码不变 –>
</div>
</div>
</template>

6.2 父组件使用插槽

可以传递文本、图片等任意内容:

<template>
<!– 插槽传递文本 –>
<Rate v-model="score">课程评分:</Rate>

<!– 插槽传递图片 –>
<Rate v-model="score">
<img width="14" src="/favicon.ico" alt="评分">
</Rate>
</template>

七、总结

本文从0到1实现了一个星级评分组件,核心知识点总结:

  • Props:组件接收外部数据的入口,规范数据类型和默认值;
  • Emit:组件向外部传递事件的方式,实现父子组件通信;
  • v-model:自定义组件的语法糖,简化props + emit的写法;
  • Slot:组件的扩展入口,支持自定义内容嵌入;
  • 组件设计思路:先实现核心功能,再逐步优化交互和扩展能力。
  • 八、扩展思考(进阶方向)

    这个基础版评分组件还可以进一步优化:

  • 支持半星自定义(而非仅通过宽度控制);
  • 替换星星字符为自定义图标(如SVG/IconFont);
  • 添加禁用状态(不可点击、悬停);
  • 支持键盘操作(方向键切换评分);
  • 增加评分文案提示(如“很差/一般/优秀”);
  • 适配移动端触摸事件。
  • 如果需要更完善的实现,可以参考Element3的Rate组件源码:Element3 评级组件。

    组件化的核心是“复用”和“解耦”,掌握了评分组件的开发思路,你可以举一反三,开发出表单、弹窗、表格等更多通用组件。希望本文能帮你理解Vue3组件化的精髓,让你的前端开发更高效!

    赞(0)
    未经允许不得转载:171主机测评 » 8、Vue3组件化实战:从0到1开发星级评分组件
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址