v-for 中的模板引用的使用思考
发布时间 2024-04-11 13:11:39

产生原因

  今天天气比较热,人整天都是懵逼状态,然后写代码过程中无意间遇到了一个比较有意思的问题,就是 v-for 中的模板引用(后面会给出更好的解决方案)。

场景复原

  假设有一个需求,需要在页面上展示一些数据,并且需要实现数据变化时,页面上的数字数字也会跟着变化。

  然后数字变化的效果我一般习惯于使用 countup.js 实现,具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<template>
<div>
<div v-for="(item, index) in itemList" :key="item.title">
<p>{{ item.title }}</p>
<!-- 使用函数模板引用 🤔 -->
<p :ref="(el) => getCountRef(el, index)">
{{ startCount(item.value, index) }}
</p>
</div>
</div>
</template>

<script setup lang="ts">
import { CountUp } from 'countup.js'

// 存储元素
const countRefsObj: any = {}

// 获取元素
const getCountRef = (el, index) => {
if (el) {
countRefsObj[index] = el
}
}

// 执行动画
const startCount = (value, index) => {
// 使用 countup.js 需要传入 DOM 元素🤔 和数字
const countup = new CountUp(countRefsObj[index], value, {
duration: 1
})
countup.start()
}

const itemList = [
{
title: 'item1',
value: '100'
},
{
title: 'item2',
value: '1974'
},
{
title: 'item3',
value: '4523'
}
]
</script>

解决方案 🎈

  上面代码中,为了让循环中的每一个元素都可以执行 countup.js 动画,不得不使用模板引用获取每个元素的 ref 对象。

  但细细想来,这样做的确是有点奇怪,为什么要获取每个元素的 ref 对象呢?其实,我们可以直接在模板中使用 countup.js 动画,不需要使用模板引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<template>
<div>
<div v-for="(item, index) in itemList" :key="item.title">
<p>{{ item.title }}</p>
<!-- 直接使用 countup.js 动画 -->
<p :id="`count-${index}`">{{ item.value }}</p>
</div>
</div>
</template>

<script setup lang="ts">
import { CountUp } from 'countup.js'

const itemList = [
{
title: 'item1',
value: '100'
},
{
title: 'item2',
value: '1974'
},
{
title: 'item3',
value: '4523'
}
]

// 循环执行动画
itemList.forEach((item, index) => {
const countup = new CountUp(`#count-${index}`, item.value, {
duration: 1
})
countup.start()
})
</script>

  进一步的,我们还可以将 countup.js 动画封装成一个组件,然后在模板中使用该组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<div>
<div v-for="(item, index) in itemList" :key="item.title">
<p>{{ item.title }}</p>
<!-- 直接使用 countup.js 组件 -->
<countup-js
:countVal="value"
:prefix="name === 'money' ? '¥' : ''"
:suffix="name === 'deal' ? '%' : ''"
></countup-js>
</div>
</div>
</template>
  • countup-js.vue 组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<template>
<span class="countup-js" ref="countRef">
{{ countVal }}
</span>
</template>

<script setup lang="ts">
import { CountUp } from 'countup.js'
import { onMounted } from 'vue'
import { ref } from 'vue'

interface Props {
countVal: string
startVal?: string
prefix?: string
suffix?: string
}

const props = withDefaults(defineProps<Props>(), {
startVal: '0',
prefix: '',
suffix: ''
})

// countup.js使用
const countRef = ref<HTMLElement>()

onMounted(() => {
const countup = new CountUp(countRef.value!, Number(props.countVal), {
startVal: Number(props.startVal),
prefix: props.prefix,
suffix: props.suffix
})

countup.start()
})
</script>

<style lang="scss" scoped>
.countup-js {
//
}
</style>

扩展阅读

1、vite 和 webpack 引入本地图片

1
2
3
4
5
6
7
8
9
10
11
<template>
<div>
<div v-for="(item, index) in itemList" :key="item.title">
<!-- vite 引入本地图片 -->
<img :src="new URL(`@/assets/${item.icon}`, import.meta.url).href" alt="" />

<!-- webpack 引入本地图片 -->
<img :src="require(`@/assets/${item.icon}`).default" alt="" />
</div>
</div>
</template>

2、opacity 和 rgba 透明度的区别

  • 设置 opacity 会影响元素以及其中的所有内容的透明度,包括背景、文本等。
  • 设置 rgba 可以单独控制一个元素的背景颜色或文本颜色的透明度,而不会影响元素内部其他内容的透明度。
上一页
2024-06-19 10:49:52
下一页