在B站看到一个Vue2的进阶视频,提到了子组件向父组件传递数据可以使用prop的方式,觉得很诧异,因为官方文档里面提到的是单向下行绑定。
父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。
但是根据实际使用的情况,有时候却需要这么操作,所以总结起来的子组件向父组件传递数据有两种方式prop和emit。
首先总结一下传统的emit方式:
// 父组件 School.vue <template> <div class="school-style"> <h2>学校名称:{{ schoolName }}</h2> <h2>学校地址:{{ address }}</h2> <h3>来自子组件Emit:{{ stuNameEmit }}</h3> <Student @CustomEventName="getDataFromSonByEmit" ></Student> </div> </template> <script> import Student from "./Student"; export default { name: "School", components: { Student: Student, }, data() { return { schoolName: "浙江大学", address: "杭州紫金港", teacherName: "陈老师", stuNameEmit: "", }; }, methods: { getDataFromSonByEmit(param) { console.log("来自子组件的数据emit:", param); this.stuNameEmit = param; }, }, }; </script>
父组件通过getDataFromSonByEmit这个函数(方法)来监听并处理子组件Student的自定义方法CustomEventName。进而可以在getDataFromSonByEmit这个函数内接收子组件“发送”来的数据并做处理。
// 子组件 Student.vue <template> <div class="student-style"> <h2>学生姓名:{{ studentName }}</h2> <h2>学生年龄:{{ age }}</h2> <button @click="sendDataByEmit">emit方式向父组件传送数据</button> </div> </template> <script> export default { name: "Student", data() { return { studentName: "张三", age: 18, }; }, props: { getFromSon: { type: Function, }, }, methods: { sendDataByEmit() { this.$emit("CustomEventName", this.studentName); }, }, }; </script> <style scoped> .student-style { background-color: pink; margin: 20px; } </style>
子组件的自定义方法CustomEventName,需要在子组件内被触发,触发之后通过this.$emit(自定义事件名称, 携带的参数)的方式向父组件通知并携带参数。
所以核心的代码:
//父组件 //父组件通过一个方法监听子组件的自定义事件,并且在方法内可以处理传递来的数据 <Student @CustomEventName="getDataFromSonByEmit"></Student> //父组件监听子组件自定义事件 methods: { getDataFromSonByEmit(param) { //这里的参数param接收的是来自子组件的参数 console.log("来自子组件的数据emit:", param); this.stuNameEmit = param; }, }, //子组件 //子组件通过emit触发自定义事件,并且传递出去数据 <button @click="sendDataByEmit">emit方式向父组件传送数据</button> methods: { sendDataByEmit() { this.$emit("CustomEventName", this.studentName); //触发子组件自定义事件并携带参数 }, },
以上是普遍使用的emit方法,另外一种就是prop方式:
//父组件 <template> <div class="school-style"> <h3>来自子组件Prop:{{ stuNameProp }}</h3> <Student :sonProp="getDataFromSonByProp"></Student> //属性绑定的是方法 </div> </template> <script> import Student from "./Student"; export default { name: "School", components: { Student }, data() { return { studentNameProp: "", }; }, methods: { getDataFromSonByProp(param) { console.log("来自子组件的数据props:", param); this.studentNameProp = param; }, }, }; </script>
注意子组件的属性sonProp绑定的是一个方法getDataFromSonByProp,这个方法是父组件的方法。一般情况,prop绑定的变量是数组或者对象或者其他任意类型的值。官网文档示例里面并没有提及(Function)方法。这里子组件属性绑定了父组件的方法。
//子组件 <template> <div class="student-style"> <h2>学生姓名:{{ studentName }}</h2> <h2>学生年龄:{{ age }}</h2> <button @click="sendDataByProps">props方式向父组件传送数据</button> </div> </template> <script> export default { name: "Student", data() { return { studentName: "张三", age: 18, }; }, props: { sonProp: { type: Function, //子组件的prop是function类型 }, }, methods: { sendDataByProps() { this.sonProp(this.studentName); //这里子组件执行的属性实际执行的是父组件的方法 }, }, }; </script>
核心的代码:
//父组件 <Student :sonProp="getDataFromSonByProp"></Student> data() { return { studentNameProp: "", }; }, methods: { getDataFromSonByProp(param) { console.log("来自子组件的数据props:", param); this.studentNameProp = param; //这里param的值就是来自子组件的studentName }, }, //子组件 <button @click="sendDataByProps">props方式向父组件传送数据</button> props: { sonProp: { type: Function, }, }, methods: { sendDataByProps() { this.sonProp(this.studentName);//这里相当于执行了父组件的方法getDataFromSonByPro }, },
子组件通过某一个方法触发了prop属性,实际就是父组件的方法,同时把子组件的数据传递到了父组件的方法内,父组件通过自己的响应式状态变量去接收这个数据。