子组件怎么监听数据修改,然后修改状态。但是父组件数据修改后子组件没有重新渲染?
这问题不知道怎么描述,不过案例很简单,bug也很明确。就是不知道怎么写T_T。
demo: https://codesandbox.io/s/happ...
父组件 home.vue
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png" />
<ul v-for="(v, k) in datas" :key="k">
<HelloWorld
@change-data="changeData"
@remove-data="removeData"
v-for="(data, i) in v"
:key="i"
:data="data"
:idx="i"
:type="k"
/>
</ul>
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
import { onMounted } from 'vue'
import hooks from '@/hooks'
export default {
name: 'Home',
components: {
HelloWorld
},
setup() {
const { datas, fetchDatas, changeData, removeData } = hooks()
onMounted(() => fetchDatas())
return { datas, changeData, removeData }
}
}
</script>
<style scoped>
ul {
width: 350px;
margin: 0 auto;
}
</style>
hooks.js
import { reactive, ref } from 'vue'
const objs = {
a: [{ name: '1' }, { name: '2' }, { name: '3' }],
b: [{ name: '4' }, { name: '5' }, { name: '6' }]
}
export default function hooks() {
const datas = ref({})
function fetchDatas() {
setTimeout(() => {
datas.value = objs
}, 300)
}
// const datas = reactive(objs)
function changeData({ data }) {
console.log('home', { ...data })
}
function removeData({ type, idx }) {
console.log('home', type, idx)
datas.value[type].splice(idx, 1)
}
return {
datas,
fetchDatas,
changeData,
removeData
}
}
子组件 HelloWorld.vue
<template>
<li class="item">
<div class="tip">change1: {{ !!data.changed }}</div>
<div class="tip">change2: {{ !!changed }}</div>
<div class="flex">
<div>
<input type="text" v-model="data.name" />
</div>
<div class="flex control">
<div @click="removeData">删除</div>
<!-- <div @click="changeData" v-if="data.changed">提交修改</div> -->
<div @click="changeData" v-if="changed">提交修改</div>
</div>
</div>
</li>
</template>
<script>
import { watch, ref, watchEffect, toRefs, reactive } from 'vue'
export default {
name: 'HelloWorld',
props: {
data: Object,
type: String,
idx: Number
},
setup(props, { emit }) {
const changed = ref(false)
// const data = reactive({ data: {} })
watchEffect(() => {
console.log('cmpt.watchEffect', props.data)
// data.data = props
// changed.value = false
})
watch(props, curr => {
console.log('cmpt.watch.idx', { ...curr })
props.data.changed = true
changed.value = true
})
function removeData() {
emit('remove-data', props)
}
function changeData() {
emit('change-data', props)
}
// const { data } = toRefs(props)
// console.log('toRefs', toRefs(data.data))
return { changed, changeData, removeData }
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
.item {
margin: 5px 0;
text-align: left;
list-style: none;
.tip {
color: #777;
font-size: 13px;
}
}
.flex {
display: flex;
> div {
margin-right: 10px;
}
}
.control {
> div {
border-radius: 4px;
background-color: #eee;
font-size: 14px;
padding: 4px 10px;
cursor: pointer;
}
}
</style>
首先传递给子组件的props是不建议更改的,所以需要在子组件中重新保存一份props的副本,如下,template中也是使用的副本
HelloWorld.vue
App.vue
中,key用的是name,最好有一个唯一的id, 使用index的话,会导致删除数据后,后续数据发生变化hooks.js
整体思想就是子组件更改的都是自身内部的数据,不点击提交和删除的话,数据就不会修改,修改数据都是在源头处理的,这里就是在hooks.js里面,然后子组件观测到父组件传过来的
props
变化后,也就是通过watchEffect
, 将其同步到内部的副本上不知道问题理解的对不对,希望能帮助到你