产生原因
今天天气比较热,人整天都是懵逼状态,然后写代码过程中无意间遇到了一个比较有意思的问题,就是 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>
|
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 可以单独控制一个元素的背景颜色或文本颜色的透明度,而不会影响元素内部其他内容的透明度。