需求
修改v-for后得到的每一项内的某个数据,视图应当重新渲染
如下面的代码
============================================
这是父组件,给子组件传了一个 类型为
interface DataType { title: string value: string show: boolean }
的数组
<template>
<Tree :options="dataSource" />
</template>
<script setup lang="ts">
import Tree from '@/test/tree/Tree.vue'
const dataSource = [
{
title: 'test1',
value: 'test1',
show: false
},
{
title: 'test2',
value: 'test2',
show: false
},
{
title: 'test3',
value: 'test3',
show: false
}
]
</script>
这是子组件
<script lang="ts" setup>
import { ref, toRefs } from 'vue'
export interface DataType {
title: string
value: string
show: boolean
}
//获取父组件传过来的props,并把数据变成响应式
const props = defineProps<{ options: DataType }>()
const { options: dataSource } = toRefs(props)
//监听点击事件,并将循环后的item数据拿到并修改
//按理来说数据已经用toRefs响应式监听了,修改数据视图应该改变才对
//但是运行代码后视图并没有刷新
const select = ref<DataType>({ title: '', value: '', show: false })
const onClick = (item: DataType) => {
item.show = !item.show
}
</script>
<template>
<div class="wrap">
<div v-for="item in dataSource" :key="item.title">
//点击这个元素将item上的属性show变成true或者false
<div class="title" @click="onClick(item)">{{ item.title }}</div>
//当item对象上的show属性为true时应该展示在视图上
<div class="content" v-if="item.show">{{ item.value }}</div>
</div>
</div>
</template>
原因
1.toRefs(props)
确实会把这个对象编程响应式,所以监听元素click事件并修改item上的show时 dataSource
的确会变
2.dataSource
变化后vue会重新执行这个组件内的代码获取新的虚拟dom然后去对比之前的dom再局部更新,
3.关键再于vue重新执行这个组件内的代码时又拿到了父组件传来的数据,而这个数据自始自终是没有发生改变的,所以得到的虚拟dom永远和第一次渲染的dom一模一样所以视图永远不变
解决方法
父组件传数据的时候应该传个ref这能解决这个问题,但是vue不推荐子组件修改外部数据
const dataSource = ref<DataType[]>([
{
title: 'test1',
value: 'test1',
show: false
},
{
title: 'test2',
value: 'test2',
show: false
},
{
title: 'test3',
value: 'test3',
show: false
}
])
思考
当数据不是重父组件传过来的,修改v-for传回的数据会不会改变视图?
答案是会的,只要这个数据用响应式ref或者reactive包裹就可以
总结
1.子组件不应该修改外部数据,应该通知父组件修改数据
2.在不方便通知父组件修改数据时,父组件应该传ref给子组件不然子组件修改的数据永远数复制品,没有效果