news 2026/9/19 14:54:47

Vuetify v-rating 星级评分组件完全指南:从基础用法到源码级原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vuetify v-rating 星级评分组件完全指南:从基础用法到源码级原理

Vuetify v-rating 星级评分组件完全指南:从基础用法到源码级原理

【免费下载链接】vuetify🐉 Vue Component Framework项目地址: https://gitcode.com/gh_mirrors/vu/vuetify

v-rating是 Vuetify 中用于收集用户评分的专业组件,它以星形图标为交互载体,将"用户反馈"这一简单指标转化为直观、可无障碍访问的界面元素,广泛应用于电商评价、产品满意度调查、内容打分等场景。本文以 Vuetify 官方文档的 Ratings 页面为主体,结合本仓库packages/vuetify/src/components/VRating/下的源码实现与测试用例,系统讲解v-rating的全部核心配置项(颜色、密度、清空、只读、悬停、图标、半星增量、尺寸、无障碍标签)、两个插槽(itemitem-label)的进阶用法,以及它背后的状态计算、键盘导航和 CSS 半星裁剪原理,让你既能"会用",也能"懂它为什么这么工作"。

组件定位与核心价值

v-rating是构建用户控件时一个专门但重要的部件。通过评分收集用户反馈,是一种简单的分析手段,却能为你的产品或应用提供大量有价值的信息。它本质上是一个"被视觉化包装的单选控件"——从源码看,每个评分项在内部渲染为一个隐藏的<input type="radio">(见 VRating.tsx),这保证了它在不依赖 JavaScript 的情况下依然具备原生表单语义,也决定了它与v-formv-model等机制的自然融合。

从组件构成上看,VRating复用了多个 Vuetify 基础组件与组合式函数:

  • 每个星形图标实际由VBtn渲染(VRating.tsx),因此天然继承了按钮的尺寸、密度、涟漪(ripple)等能力;
  • 密度与尺寸分别来自makeDensityPropsmakeSizeProps(VRating.tsx),与 Vuetify 全局约定保持一致;
  • 主题支持通过makeThemePropsprovideTheme接入(VRating.tsx)。

基础用法

v-rating提供了一个简单直观的接口来收集用户反馈。最基本的使用方式只需绑定v-model

<template> <div class="text-center"> <v-rating v-model="rating"></v-rating> </div> </template> <script setup> import { ref } from 'vue' const rating = ref(3) </script>

默认渲染 5 颗星,v-model的值为数值型评分。在官方文档的交互示例(usage.vue)中,你还可以实时切换half-incrementshoverreadonlylengthsizecoloractive-color等配置来观察组件行为,并把生成的代码直接复制到你的项目中。

从源码角度理解这个"简单接口"背后的逻辑:

  • 值规范化normalizedValue通过clamp(parseFloat(rating.value), 0, Number(props.length))把任意输入钳制在0 ~ length区间(VRating.tsx),即使你传入越界值也不会渲染出错;
  • 状态计算itemState依据"当前值 >= 星值"判断每颗星是否填充(isFilled),并据此决定使用fullIcon还是emptyIcon(VRating.tsx);
  • 事件绑定eventState为每个评分档位生成onMouseenteronMouseleaveonClick处理器,其中点击回调会在disabledreadonly时直接短路返回(VRating.tsx)。

对应地,浏览器端测试 VRating.spec.browser.tsx 验证了"点击第 4 个评分项后v-model值变为 4""响应v-model外部变化重新渲染图标"等基础行为。

API 一览

v-rating是评分功能的唯一核心组件,其全部 props、slots 与事件在 VRating.tsx 中集中定义。下表汇总了所有可用配置项:

| Prop | 类型 | 默认值 | 说明 | | - | - | - | - | |model-value|number \| string|0| 当前评分值,支持v-model双向绑定 | |length|number \| string|5| 评分项(星星)的数量 | |color|string| — | 未选中/填充项的基础颜色 | |active-color|string| — | 已填充项的颜色,未设置时回退到color| |clearable|boolean|false| 点击当前评分值可将其重置为0| |readonly|boolean|false| 只读模式,禁止修改评分 | |disabled|boolean|false| 禁用整个组件 | |hover|boolean|false| 悬停时图标变为实心并略微放大 | |half-increments|boolean|false| 支持0.5粒度评分 | |item-labels|string[]| — | 每个评分项的标签文本数组 | |item-label-position|'top' \| 'bottom'|'top'| 标签显示在图标上方或下方 | |empty-icon|IconValue|$ratingEmpty| 未填充状态使用的图标 | |full-icon|IconValue|$ratingFull| 已填充状态使用的图标 | |item-aria-label|string|$vuetify.rating.ariaLabel.item| 供辅助技术使用的每项无障碍标签 | |ripple|boolean|false| 点击时是否显示涟漪效果 | |name|string| 自动生成 | 内部 radio 输入框的name属性 |

组件还会自动继承densitysizetagtheme以及通用组件属性(class/style等)。事件方面仅有一个update:model-value(VRating.tsx),用于配合v-model

其中两个图标默认值$ratingEmpty$ratingFull是 Vuetify 主题中注册的图标别名,会跟随当前图标的默认集(如 Material Design Icons)解析为具体图标,你也可以用任意自定义图标覆盖。

Props 实战详解

颜色:Color

v-rating的颜色可以随心定制,选中与未选中状态的颜色可以分别设置active-color控制已填充星星的颜色,color控制未填充星星(以及作为其回退色)的颜色。

<template> <div class="text-center"> <v-rating v-model="rating" active-color="blue" color="orange-lighten-1" ></v-rating> </div> </template> <script setup> import { ref } from 'vue' const rating = ref(3) </script>

在源码中,activeColor = props.activeColor ?? props.color,而每颗星的实际颜色由(isFilled || isHovered) ? activeColor : props.color决定(VRating.tsx)——即填充状态和悬停状态都使用active-color。完整的官方示例见 prop-color.vue。

密度:Density

使用densityprop 控制v-rating各项在垂直方向占用的空间,可选值defaultcomfortablecompact

<template> <div class="d-flex flex-column align-center justify-center"> <v-rating v-model="rating" class="ma-2" density="default"></v-rating> <v-rating v-model="rating" class="ma-2" density="comfortable"></v-rating> <v-rating v-model="rating" class="ma-2" density="compact"></v-rating> </div> </template>

该 prop 直接透传给内部每个VBtn(VRating.tsx),因此密度的视觉表现与 Vuetify 其他组件保持一致。完整示例见 prop-density.vue。

清空:Clearable

点击当前评分值可以将评分重置为0

<template> <div class="text-center"> <v-rating v-model="rating" clearable></v-rating> </div> </template> <script setup> import { ref } from 'vue' const rating = ref(3) </script>

其实现逻辑一行即可说明:rating.value = normalizedValue.value === value && props.clearable ? 0 : value(VRating.tsx)——当再次点击当前值且开启了clearable时归零,否则设置为新值。测试用例 VRating.spec.browser.tsx 验证了"未开启时点击已选值不变化、开启后点击同一项归零"的完整行为。

只读:Readonly

对于不允许修改的评分展示(如只读的商品平均分),使用readonlyprop:

<template> <div class="text-center"> <v-rating v-model="rating" readonly></v-rating> </div> </template>

源码中readonlydisabled会在点击与键盘事件处理器中直接返回(VRating.tsx 与 L157),同时根元素会加上v-rating--readonly类。样式层通过.v-rating--readonly { pointer-events: none }直接屏蔽所有指针交互(VRating.sass),实现双重防护。测试用例分别验证了只读状态下点击与方向键都不会改变值(VRating.spec.browser.tsx)。

悬停效果:Hover

使用hoverprop 后,鼠标悬停到的评分图标会变为实心颜色,并略微放大

<template> <div class="text-center"> <v-rating v-model="rating" hover></v-rating> </div> </template>

放大效果来自样式层:悬停时.v-rating--hover作用域下的按钮应用transform缩放(VRating.sass),该 transform 值定义在 _variables.scss 中。悬停状态由hoverIndex追踪:鼠标进入某个档位时记录下标,离开时重置为-1(VRating.tsx)。测试 VRating.spec.browser.tsx 通过userEvent.hover验证了悬停第 3 个图标后前 3 个图标全部变为实心。

标签:Labels

v-rating可以在每个评分项的上方或下方显示标签,通过item-label-position控制位置(默认top):

<template> <div class="d-flex align-center justify-center flex-column"> <v-rating v-model="rating" :item-labels="['sad', '', '', '', 'happy']" class="ma-2" item-label-position="top" ></v-rating> <v-rating v-model="rating" :item-labels="['sad', '', '', '', 'happy']" class="ma-2" item-label-position="bottom" ></v-rating> </div> </template>

item-labels是长度与length对应的字符串数组,空字符串会渲染为占位空格以保持对齐(VRating.tsx)。完整示例见 prop-item-labels.vue。在showcase测试故事中也有对应演示(VRating.spec.browser.tsx)。

图标:Icons

可以使用自定义图标来替换默认的星形。图标由三个 prop 控制:empty-icon(未填充)、full-icon(已填充),配合half-increments时半星图标通过 CSS 裁剪实现(见下文"半星增量"),因此并不需要一个单独的half-iconprop:

<template> <div class="text-center"> <v-rating v-model="rating" empty-icon="mdi-circle-outline" full-icon="mdi-circle" half-increments hover ></v-rating> </div> </template>

图标值支持 Vuetify 的IconValue类型,即可以是图标名字符串,也可以是渲染函数或 SVG 组件(VRating.tsx)。完整示例见 prop-icons.vue。

数量:Length

通过lengthprop 改变评分项的数量:

<template> <div class="text-center"> <v-rating v-model="rating" length="10"></v-rating> </div> </template>

源码中使用createRange(Number(props.length), 1)生成从 1 到 length 的序列(VRating.tsx),配合normalizedValue的钳制逻辑,length 也可以在运行时动态变化。示例见 prop-length.vue。

半星增量:Half increments

half-incrementsprop 提升了评分粒度,允许出现.5这样的值:

<template> <div class="text-center"> <v-rating v-model="rating" half-increments hover ></v-rating> <pre>{{ rating }}</pre> </div> </template>

它的实现非常巧妙,值得展开说明:

  1. 档位翻倍increments会把每个整数档位展开为[v - 0.5, v],例如 5 颗星会生成 10 个可点击档位(VRating.tsx)。测试断言此时页面上出现 10 个隐藏 radio 输入框(VRating.spec.browser.tsx);
  2. CSS 裁剪绘制半星:半星档位渲染时带有v-rating__item--half类,样式层使用clip-path: polygon(0 0, 50% 0, 50% 100%, 0 100%)只显示图标左半部分,并配合绝对定位叠加在整星之上(VRating.sass),从而在视觉上呈现"左实右空"的半星效果;
  3. 键盘步进:方向键的步长会随halfIncrements变为0.5(VRating.tsx)。

测试验证点击第 4 个半星档位得到3.5分(VRating.spec.browser.tsx)。完整示例见 prop-half-increments.vue。

尺寸:Size

sizeprop 用于控制图标大小,可以复用v-icon的尺寸值,也可以传入任意数值:

<v-rating v-model="rating" size="64"></v-rating>

size来自makeSizeProps,并直接透传给内部每个VBtn(VRating.tsx)。测试用例专门验证了half-increments与自定义size="64"组合使用时的行为(VRating.spec.browser.tsx)。官方交互示例 usage.vue 中可拖动滑块在 16~128 之间实时预览尺寸变化。

无障碍标签:Aria Label

通过item-aria-labelprop 为每个评分项提供辅助技术(屏幕阅读器)可读的标签:

<v-rating v-model="rating" :item-aria-label="(value) => `Rate ${value} out of 5`" ></v-rating>

默认值为$vuetify.rating.ariaLabel.item(一个本地化文案键)。源码中该标签会同时渲染在隐藏文本v-rating__hidden中,并作为每个VBtnaria-label(VRating.tsx 与 L217),确保辅助技术与普通用户获得一致的信息。文案通过useLocalet函数按当前语言解析。

Slots 深度定制

插槽为评分组件提供了高级定制能力,让你完全掌控评分的展示形式。

Item 插槽

item插槽允许你完全替换每个评分项的默认渲染内容。插槽接收以下属性:

  • value:当前项的值
  • index:当前项的索引
  • isFilled:该项是否处于填充状态
  • isHovered:该项是否处于悬停状态
  • icon:当前应显示的图标
  • color/activeColor:颜色信息
  • props:透传给内部VBtn的完整按钮属性(含键盘与无障碍处理)
  • rating:当前总分
<template> <div class="text-center"> <v-rating v-model="rating"> <template v-slot:item="props"> <v-icon :color="props.isFilled ? colors[props.index] : 'grey-lighten-1'" size="large" > {{ props.isFilled ? 'mdi-star-circle' : 'mdi-star-circle-outline' }} </v-icon> </template> </v-rating> </div> </template> <script setup> import { ref } from 'vue' const colors = ['green', 'purple', 'orange', 'indigo', 'red'] const rating = ref(4.5) </script>

该示例让每颗填充的星星使用不同的颜色。注意:插槽内容默认仍然包裹在带点击/悬停/键盘事件的<label>中,评分交互依然生效,props中还携带了tabindex与键盘处理器供自定义按钮使用(VRating.tsx)。完整示例见 slot-item.vue,showcase故事中也有一个用VBtn渲染数字评分的变体(VRating.spec.browser.tsx)。

自定义标签插槽:item-label

item-label插槽让你在评分项旁展示任意内容。插槽接收{ value, index, label }属性,其中label来自item-labels数组中的对应项:

<template> <v-rating v-model="rating"> <template v-slot:item-label="props"> C{{ props.value }} </template> </v-rating> </template>

当该插槽存在时,它会优先生效并覆盖item-labels数组的默认渲染(VRating.tsx)。测试中的展示故事将标签渲染为C1C2等形式(VRating.spec.browser.tsx),示例文件见 slot-item-label.vue。

进阶场景:卡片评分

评分组件非常适合与产品类界面搭配,用于收集和展示客户反馈。官方文档提供了两个组合示例:

  • misc-card.vue:将v-rating嵌入v-card,结合v-avatarv-list等组件展示单条用户评价;
  • misc-card-overview.vue:卡片式评分总览。

这类场景通常还会配合readonly(仅展示已有评分)或clearable(允许修改),并可参考 cards 与 icons 文档组合出更丰富的界面。文档中原本规划的 "Advanced usage" 示例(misc-advanced.vue,见 ratings.md 中的注释)目前仍处于注释状态,尚未正式开放。

键盘导航与无障碍设计

v-rating提供了完整的键盘操作支持,这也是它作为表单控件的重要一环:

  • Tab 聚焦:当前评分值对应的项拥有tabindex="0",其余项为-1,确保只聚焦一个锚点(VRating.tsx);
  • 方向键调节ArrowRight增加值(上限为length),ArrowLeft减少值(下限为 0),步长为 1 或半星模式下的 0.5,调节后焦点会跟随移动到新的当前项(VRating.tsx);
  • 空格选中VBtn自身的按钮语义天然支持空格触发点击;
  • 隐藏 radio 语义:每个档位对应一个隐藏的 radio 输入,配合name属性构成标准的单选组,name未指定时自动生成v-rating-{uid}(VRating.tsx)。

浏览器测试 VRating.spec.browser.tsx 完整覆盖了"Tab 进入 → 空格评分 → Tab 离开 → Shift+Tab 返回 → 方向键调节并移动焦点"的整条键盘链路。

样式体系速览

v-rating的样式集中在 VRating.sass 与 _variables.scss 中,几个关键设计点:

  • 根元素为display: inline-flexmax-width: 100%,可随内容自适应;
  • 每个评分项外包一层v-rating__wrapper,标签在顶部或底部时分别使用flex-direction: columncolumn-reverse实现上下布局;
  • 内部按钮为plain变体并设置较低不透明度,填充/悬停时通过图标切换与transform缩放表达状态;
  • 半星通过clip-path裁剪叠加实现;
  • 只读时以pointer-events: none禁用指针交互。

总结

v-rating是 Vuetify 中一个"小而精"的组件:通过v-modellengthcolor/active-colorclearablereadonlyhoverhalf-incrementssizeitem-labels、自定义图标与无障碍标签等十余个配置项,即可覆盖从简单的五星打分到带半星、自定义图标、逐项标签和完全自定义渲染的复杂评分场景。其底层以隐藏 radio 输入 +VBtn图标按钮 + CSS 裁剪半星的组合实现,兼顾了表单语义、无障碍访问与视觉表现力,相关实现细节均可在 VRating.tsx、VRating.sass 与 VRating.spec.browser.tsx 中进一步查阅。配合卡片、图标等组件组合使用,即可快速构建专业的产品反馈界面。

【免费下载链接】vuetify🐉 Vue Component Framework项目地址: https://gitcode.com/gh_mirrors/vu/vuetify

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 14:47:51

AI时代PLC工程师的生存法则:从写代码到搞定产线

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 14:47:46

软件工程术语库:面向协作的语义操作系统设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 14:47:35

Eclipse报错cannot be resolved to a type?从JDK到依赖的完整排查指南

简介&#xff1a;面向Java开发者和Eclipse用户的一份实用排错指南&#xff0c;专门解决项目导入或编译时常见的“xxx cannot be resolved to a type”错误。文档从实际开发场景出发&#xff0c;系统梳理四类典型原因&#xff1a;JDK版本不匹配或不存在、Jar包缺失或相互冲突、E…

作者头像 李华
网站建设 2026/9/19 14:46:44

SEW S系列减速机样本手册解析:型号命名、参数表与选型校验

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 14:41:13

智能控电柜实时控制架构设计与实现

简介&#xff1a;本资源是一份面向嵌入式系统开发工程师与工业自动化软件架构师的《智能控电柜系统软件架构设计说明书》&#xff0c;聚焦于电力控制类嵌入式设备的顶层软件结构设计&#xff0c;解决多模块协同、实时性保障与可维护性提升等核心工程问题。文档为单文件Word格式…

作者头像 李华
网站建设 2026/9/19 14:40:38

B站视频旋转90度:控制台CSS transform原理与实战方案

本来竖屏的素材传到B站&#xff0c;播放器里却横着显示&#xff0c;一半画面被裁掉&#xff1b;或者录屏时方向没锁住&#xff0c;画面躺平了&#xff1b;又或者只是想临时把某个直播画面转个方向看&#xff0c;而B站设置里根本没有"旋转视频"这个按钮。我最初碰到这…

作者头像 李华