制作一个有趣的打分小组件,评价为“五星上将”,理解计算属性
正文
今天来制作一个有趣的交互式小操作
为店铺或者商品的打分小组件
(给出如下的例子:美团订单的评分以及抖音团购的评分)
自己来写几个星星
从易到难,先写出几个基础的星星
<template>
<div>
{{ rate }}
</div>
</template>
<script setup>
import { defineProps,computed } from 'vue'
let props = defineProps({
value: Number,
})
let rate = computed(() => "★★★★★☆☆☆☆☆".slice(5-props.value, 10-props.value))
</script>
<style lang="css" scoped>
</style>
使用了Vue的defineProps
和computed
方法。defineProps
用于定义组件的props,
定义了一个名为value
的Number类型prop。
computed
方法创建了一个计算属性rate
,它根据props.value
的值来截取字符串"★★★★★☆☆☆☆☆"的一部分,从而动态地生成评分的星星图标。
最终,在模板中通过双括号语法{{ rate }}
来显示计算后的评分。
defineProps
是Vue 3中用于在组合API (Composition API
) 中声明组件props的一个重要函数。它允许你显式地定义组件预期从父组件接收的属性(props),并为这些属性提供类型检查、默认值以及其他元信息。以下是
defineProps
的主要作用:
- 类型检查:
defineProps
让你能够为每个prop指定其预期的类型,例如String
,Number
,Boolean
,Array
,Object
,Function
, 或者自定义构造函数。这有助于防止错误类型的值被传递给组件。- 默认值: 你可以为每个prop定义默认值,当父组件没有传递该prop时,组件将使用这个默认值。
- 响应式处理:
defineProps
返回一个响应式的代理对象,这意味着当父组件更改prop的值时,子组件会自动更新以反映新的状态。
computed
是Vue.js框架中的一种属性类型,用于在组件中定义依赖于其他数据的计算属性。
computed
在该代码片段中主要负责根据输入的value
属性动态计算出一个表示评分的字符串,
在主页面中生成出分别为5、4、3、2颗实星星
<template>
<div>
<Rate :value="5"/>
<Rate :value="4"/>
<Rate :value="3"/>
<Rate :value="2"/>
</div>
</template>
<script setup>
import { ref } from "vue";
import Rate from "./components/Rate0.vue";
</script>
而我们需要将这些星星全都变为响应式的才能发挥出其评分的作用
需要明确的是,星星的显示样式(实心或空心)应该基于它们的索引和当前的评分值(props.value
)。通常情况下,索引小于等于评分值的星星应该是实心的,其他星星则是空心的。
为了实现点击或鼠标悬停时改变星星样式的功能,我们可以这样做:
- 定义星星的样式:在CSS中,我们可以定义两种样式类,一种是
.filled
用于实心星星,另一种是.hollow
(或不加这个类,直接用默认样式)用于空心星星。 - 使用
v-for
渲染星星:在模板中,我们使用v-for
指令来遍历星星的数量(比如5颗)。对于每一颗星星,我们根据它的索引和评分值来决定是否应用.filled
样式类。 - 添加点击和鼠标悬停事件:我们可以为每颗星星添加
@click
和@mouseover
事件监听器。当星星被点击时,我们更新评分值并重新渲染星星;当鼠标悬停在星星上时,我们临时改变星星的样式为实心(如果需要预览效果的话)。
<template>
<div :style="fontStyle"><!-- style 绑定将把 fontStyle 数据属性(或计算属性) -->
<div class="rate" @click="changeJudge" @mouseout="mouseOut">
<span @mouseover="mouseOver(num)" v-for="num in 5" key = "num">☆</span>
<span class="hollow" :style="fontwidth">
<span @click="onRate(num)" @mouseover="mouseOver(num)" v-for="num in 5" key = "num">★</span>
</span>
<!-- {{ rate }} -->
</div>
</div>
<!-- <button @click="onRate(1)">1</button> -->
</template>
<script setup>
// 新的需求 添加主题 改变不同风格的⭐
import { ref,defineProps,computed,defineEmits } from 'vue'
// 自身的状态
let props = defineProps({
value: Number,
theme: {type:String,default:'purple'}
})
let width = ref(props.value)
let fontwidth = computed(() => `width: ${width.value}em;`)
let themeObj = {
orange: '#ffa500',
blue: '#0000ff',
green: '#008000',
red: '#ff0000',
purple: '#800080',
pink: '#ffc0cb',
yellow: '#ffff00',
gray: '#808080',
black: '#000000',
white: '#ffffff',
}
let rate = computed(() => "★★★★★☆☆☆☆☆".slice(5-props.value, 10-props.value))
const fontStyle = computed(() => {
return `color: ${themeObj[props.theme]}`//themeObj配置项
})
let emits = defineEmits(["update-rate"])
const onRate = (num) => {
emits("update-rate", num)
}
let Judge = false
const changeJudge = () => {
Judge =! Judge
}
function mouseOver(i) {
if(Judge){
return
}
width.value = i
}
function mouseOut() {
width.value = props.value
}
</script>
<style>
.rate {
position: relative;
display: inline-block;
}
.rate span {
/* letter-spacing: 3px; */
display: inline-block;
width: 1rem;
height: 22px;
overflow: hidden;
}
.rate > span.hollow {
position: absolute;
display:inline-block;
top:0;
left:0;
width:0;
overflow:hidden;
}
</style>
1. 模板部分 (<template>
)
-
外层
div
:使用了:style="fontStyle"
来动态绑定样式,这里预期是绑定星星的颜色 -
评分区域:
<div class="rate">
内部包含了两个span
元素,分别用于显示已评分的星星(实心)和未评分的星星(空心)。- 实心星星:通过
v-for="num in 5"
循环生成5个星星,每个星星的mouseover
事件触发mouseOver
方法,用于预览评分效果。 - 空心星星:包裹在
span class="hollow"
内,通过计算属性fontwidth
(但这里其实用错了,应该是用来控制空心星星的显示宽度)和另一个v-for
循环生成。这些星星的click
和mouseover
事件同样触发相关方法。
- 实心星星:通过
2. 脚本部分 (<script setup>
)
-
Props:定义了
value
(当前评分)和theme
(主题颜色)两个props。 -
Refs和Computed Properties:
width
:用于临时存储鼠标悬停时的评分预览值。fontwidth
:用于控制空心星星的显示宽度。themeObj
:定义了不同主题对应的颜色。fontStyle
:同样是语法错误,应该返回一个对象,如{ color: themeObj[props.theme] }
,用于绑定到外层div
的样式上。
-
Emits:定义了
update-rate
事件,用于子组件向父组件通信评分的变化。 -
Methods:
onRate
:点击星星时触发,通过emits
发送评分到父组件。changeJudge
:切换一个名为Judge
的变量,。mouseOver
和mouseOut
:分别处理鼠标悬停和移出时的逻辑,mouseOver
用于预览评分,mouseOut
用于恢复原始评分显示。
3. 样式部分 (<style>
)
.rate
:定义了评分区域的基本样式,包括位置、显示方式和内联块级元素的特性。.rate span
:为星星设置了宽度、高度和溢出隐藏,确保星星按照固定大小显示。.rate > span.hollow
:特别为空心星星设置了绝对定位,使其能够覆盖在实心星星之上,并通过改变宽度来显示未评分的星星。
渲染出评分的星星吧
<template>
<div>
<Rate0 :value="score" theme="orange" @update-rate="updata" />
</div>
</template>
<script setup>
import { ref } from "vue";
import Rate0 from "./components/Rate1.vue";
let score = ref(0);
function updata(num){
score.value = num;
}
</script>
<Rate0>
组件通过 :value
绑定了 score
的值,通过 theme
属性设置了样式主题为橙色,监听了 update-rate
事件,当该事件触发时,updata
函数会被调用,从而更新 score
的值。这种机制允许 Rate0
组件内部改变其绑定的值,并且这种变化可以被外部组件感知和响应。
默认为全是空星,鼠标移到哪里就变为鼠标下的实星数
结语
<div :style="fontStyle">
在Vue.js中,:style
是一个绑定指令,用于将CSS样式动态地应用到元素上。这里,fontStyle
是一个在Vue实例的data
选项中定义的对象,该对象包含了要应用到<div>
元素上的CSS样式属性。( 绑定为 fontStyle 数据属性(或计算属性))
let props = defineProps({
value: Number,
theme: {type:String,default:'purple'}
})
defineProps
是一个用于在组件内部声明接收的 prop 的方法,- defineProps
是一个函数,它接收一个对象作为参数,这个对象描述了组件期望接收的所有 prop 的类型和默认值。
let emits = defineEmits(["update-rate"])
const onRate = (num) => {
emits("update-rate", num)
}
defineEmits
提供了一种机制,让组件可以与外部环境(通常是父组件)进行通信,通过触发自定义事件来通知状态的变化或请求某些操作。当子组件触发 "update-rate"
事件时,updata
函数将被调用,并接收到从子组件传递过来的新评分值作为参数。
转载自:https://juejin.cn/post/7389653171961053199