前言:相信我,只要你仔细看完本篇文章,你将不在惧怕父子组件传值的问题,本篇文章将具体分析父子组件传值出现问题的原因,所以撸起袖子加油干吧!
在开发Vue项目的过程中,最经常遇到的问题无非就是父组件数据未同步到子组件中、子组件状态改变父组件未同步数据,导致这些问题的主要原因是父组件向子组件传值是一个单向传递的过程,不像数据双向绑定那样省事,这就需要我们手动的去监听一方的数据变化并手动更新。
一、父组件数据更新,未同步到子组件
一般出现这种情况是因为,子组件上绑定的数据是通过间接的渲染到子组件上的(比如通过一个中间变量在created、mounted钩子函数中赋值然后再渲染到子组件的中),这种情况下因为钩子created等钩子函数在初始化完成后就不会再执行了,之后如果父组件数据发生改变,自然而然就不会同步到子组件中。
但是如果子组件直接拿着props中传的数据进行渲染就不会出现数据不同步更新的问题,但是有的时候我们需要对子组件的数据进行简单的处理之后进行渲染,所以对于特殊情况我们要特殊处理。
案例一:子组件直接渲染绑定数据,该种情况不会出现子组件数据不同步的问题。
<-- 父组件代码 -->
<template>
<div class="container">
<h1>子组件的testData值:</h1>
<Test :testData="testData" />
<button @click="goLogin()">去登录</button>
<button @click="goRegester()">去注册</button>
<br />
<h1>父组件的testData值:</h1>
{{ testData }}
</div>
</template>
<script>
import Test from "@/components/test.vue";
export default {
name: "test",
components: { Test },
data() {
return {
testData: "欢迎注册!",
};
},
created() {
setTimeout(()=>{
this.goLogin();
}, 3000)
},
methods: {
goLogin() {
this.testData = "欢迎登录!";
},
goRegester() {
this.testData = "欢迎注册!";
},
},
};
</script>
<-- 子组件 -->
<template>
<div class="containetr">
{{ testData }}
</div>
</template>
<script>
export default {
name: "test",
props: ["testData"],
};
</script>
该实例中,程序运行,触发定时器三秒后,父组件和组件的中的值都将更新未“欢迎登录”。
案例一:子组件间接渲染绑定数据,该种情况如果不对子组件绑定的数据进行监听或者使用系统的钩子函数进行强制更新数据的话,子组件的数据将不会得到更新。
<-- 子组件 -->
<template>
<div class="containetr">
{{ data }}
</div>
</template>
<script>
export default {
name: "test",
data() {
return {
data: "初始值",
};
},
props: ["testData"],
created() {
// 首次渲染父组件初始数据
this.data = this.testData;
},
watch: {
testData(newValue, oldVlue) {
//newValue为新值,oldVlue为旧值;
this.data = newValue;
},
},
};
</script>
案例三:$nextTick 是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 $nextTick,则可以在回调中获取更新后的 DOM 这样重新渲染就会是最新数据了。(利用v-if的重新渲染机制)
<-- 父组件 -->
<template>
<div class="container">
<h1>子组件的testData值:</h1>
<Test :testData="testData" v-if="someShow" />
<button @click="goLogin()">去登录</button>
<button @click="goRegester()">去注册</button>
<br />
<h1>父组件的testData值:</h1>
{{ testData }}
</div>
</template>
<script>
import Test from "@/components/test.vue";
export default {
name: "test",
components: { Test },
data() {
return {
testData: "欢迎注册!",
someShow: true,
};
},
methods: {
goLogin() {
this.testData = "欢迎登录!";
this.someShow = false;
this.$nextTick(()=> {
this.someShow = true;
});
},
goRegester() {
this.testData = "欢迎注册!";
this.someShow = false;
this.$nextTick(()=> {
this.someShow = true;
});
},
},
};
</script>
二、子组件数据更新,未同步到父组件
上面说到父组件与子组件之间的传值是一个单向传递的过程,而并非一个双向绑定的结果,即:父组件更新数据可以实时的同步到子组件的prop对象中,但是子组件却不能去直接修改子组件绑定的值。即便是修改了也不会同步到父组件中,反而后台会报一个不能直接修改prop对象数据的错误。
案例4:使用.sync 修饰符搭配$emit,解决子组件数据同步到父组件的问题,关于这两个属性方法的使用可在官网文档.sync修饰符章节查看。
<-- 父组件 -->
<template>
<div class="container">
<h1>子组件的testData值:</h1>
<Test :testData.sync="testData" />
<button @click="goLogin()">去登录</button>
<button @click="goRegester()">去注册</button>
<br />
<h1>父组件的testData值:</h1>
{{ testData }}
</div>
</template>
<script>
import Test from "@/components/test.vue";
export default {
name: "test",
components: { Test },
data() {
return {
testData: "欢迎注册!",
};
},
methods: {
goLogin() {
this.testData = "欢迎登录!";
},
goRegester() {
this.testData = "欢迎注册!";
},
},
};
</script>
<-- 子组件 -->
<template>
<div class="containetr">
{{ data }}
<input type="text" v-model="data" />
</div>
</template>
<script>
export default {
name: "test",
data() {
return {
data: "初始值",
};
},
props: ["testData"],
created() {
// 首次渲染父组件初始数据
this.data = this.testData;
},
watch: {
data(newValue, oldVlue) {
//newValue为新值,oldVlue为旧值;
this.$emit("update:testData", newValue);
},
},
};
</script>
案例5:综上所述实现父组件与子组件的双向数据绑定(子组件代码同上一案例)
<-- 父组件 -->
<template>
<div class="container">
<h1>子组件的testData值:</h1>
<Test :testData.sync="testData" v-if="someShow" />
<button @click="goLogin()">去登录</button>
<button @click="goRegester()">去注册</button>
<br />
<h1>父组件的testData值:</h1>
{{ testData }}
</div>
</template>
<script>
import Test from "@/components/test.vue";
export default {
name: "test",
components: { Test },
data() {
return {
testData: "欢迎注册!",
someShow: true,
};
},
methods: {
goLogin() {
this.testData = "欢迎登录!";
this.someShow = false;
this.$nextTick(()=> {
this.someShow = true;
});
},
goRegester() {
this.testData = "欢迎注册!";
this.someShow = false;
this.$nextTick(()=> {
this.someShow = true;
});
},
},
};
</script>
三、最后的阐述
为什么vue没有实现prop数据的双向绑定呢?主要是因为,如果是双向绑定的,就会带来维护上的问题,子组件可以很容易的改变父组件数据,对于父组件数据的维护会变得更加复杂,所以说如果有数据更新的需求,就需要手动更新数据,这样可以清楚的看到子组件执行了更新父组件的数据,对于后期的维护是有益的。
想了解Vue3的新增知识请查看这里:Vue3新增知识汇总