1. 前言
最近在修 Bug 时遇到一个很奇怪的问题,简单来说就是:在使用 watch
监听 props
的一个类型为数组的数据时,props
传值没有变,但却触发了 watch
回调。
2. 复现 Bug
假设我们实现一个选择器组件:
Select.vue
<template>
<div>
<div>{{ modelValue ?? "-" }}</div>
<div v-for="option in options" :key="option.value" @click="$emit('update:modelValue', option.value)">
{{ option.label }}
</div>
</div>
</template>
<script setup>
import { watch } from "vue";
const props = defineProps({
modelValue: String,
options: Array
});
const emit = defineEmits(["update:modelValue"]);
watch(
() => props.options,
() => {
console.log("options changed");
},
{
onTrack(e) {
console.log(e);
},
onTrigger(e) {
console.log(e);
}
}
);
</script>
引用该组件:
Demo.vue
<script setup>
import { ref } from "vue";
import Select from "./Select.vue";
const valueRef = ref(null);
function createOptions() {
return [
{ label: "A", value: "A" },
{ label: "B", value: "B" },
{ label: "C", value: "C" }
];
}
</script>
<template>
<Select v-model="valueRef" :options="createOptions()" />
</template>
效果如下:
通过 onTrack()
调试信息发现,() => props.options
以 get
方式监听 options
。
当我们点击 ABC 中某一个选项,那么就短横线就应该显示为那一项的值,我们点击 A:
A 显示出来了,但控制台竟然打印了 options changed
。
具体原因目前不详,但可以使用以下两种方式避免该问题。
3. 解决方案
3.1 props 直接传数组
引用组件时,props
传值直接传数组:
Demo.vue
.
.
.
<template>
<Select v-model="valueRef" :options='[
{ label: "A", value: "A" },
{ label: "B", value: "B" },
{ label: "C", value: "C" }
]' />
</template>
3.2 如果值由函数生成,先赋值给变量
.
.
.
const options = createOptions();
</script>
<template>
<Select v-model="valueRef" :options="options" />
</template>