最近我负责的项目已经迭代到第四版了,我作为一个没啥经验的小菜鸟也成长了很多。
在这一版开发开始之前,我老大就要求我在开发过程中尽量实现组件化,因此,我也遇到了很多问题,但基本都解决了,所以趁周末把这些经验总结一下。
Vue中父子组件传值的方法大体上可以分为2类:
1. 通过第三方转接,比如vuex,eventbus以及浏览器的localStorage,sessionStorage等;
2. 父子组件之间直接进行数据交换
第一种方法我用的比较多的就是vuex 以及localStorage,sessionStorage,需要注意的一点是vuex中数据改变最好使用mutation进行提交。
这次主要说说我使用第二种方法遇到的一些问题以及解决方法。
首先父子组件传值有3种写法
第一种,如果传入值是一个静态变量,可以直接在标签上写:变量名=值,这样写只能算是单向数据绑定。
父组件:
<template> <div> <child msg="一条消息"></child> </div> </template> <script> import child from './child' export default { components:{ child } } </script>
子组件在props中进行接收
子组件:
<template> <div> <p>{{msg}}</p> </div> </template> <script> export default { props: ["msg"] }; </script>
第二种,也是最常用的一种,在标签上使用:或者v-bind进行数据绑定,子组件还是用props接收,如果想要更改数据,需要在父组件中定义更改数据的函数,然后用v-on或者@绑定到子组件上,子组件中使用this.$emit(方法名字符串,唯一参数)的方式调用。
这种方法最常见,也用的最多,所以就不再贴代码了。
第三种,使用v-model进行数据绑定
父组件:
<template> <div> <child msg="一条消息" v-model="number"></child> </div> </template> <script> import child from "./child"; export default { components: { child }, data() { return { number: 1, flag: false }; } }; </script>
子组件:
<template> <div> <p>{{msg}}</p> <p>{{number}}</p> <button @click="change">number+1</button> </div> </template> <script> export default { model: { prop: "number", //变量名 event: "aa" //自定义的事件名 }, props: { msg: String, number: Number }, methods: { change() { this.$emit("aa", this.number + 1); } } }; </script>
v-model还有一种写法,就是在子组件中默认的变量value进行变量接收,父组件写法不变
子组件:
<template> <div> <p>{{msg}}</p> <!-- <p>{{number}}</p> --> <p>{{value}}</p> <button @click="change">number+1</button> </div> </template> <script> export default { //model: { // prop: "number", //变量名 // event: "aa" //自定义的事件名 //}, props: { msg: String, value: Number //接收number }, methods: { change() { // this.$emit("aa", this.number + 1); this.$emit("input", this.value + 1); //必须使用默认的input事件进行变量更改提交 } } }; </script>
这种写法的优势在于,如果你需要父组件传一个简单的变量给子组件,并且需要父子组件同步修改,可以省略掉父组件v-on绑定给子组件的函数
mint-ui中popup组件中控制组件显示与隐藏就是用的这种方法。
但是父组件不会总是传递一个简单的基本变量给子组件,很多情况是父组件传一个对象或者数组,或者对象、数组嵌套的变量给子组件,子组件使用v-for循环渲染出一个列表,
并且列表项上会有一些可点击的部分,用来更改相应的数据,这时候,如果要父组件为每一个修改数据的行为都写一个函数,并且用v-on绑定到子组件上,那么当我在其他页面也用到这个组件,又要把这些函数拿到另外一个文件中,实在是不方便,且不优雅。
这个时候我们就要考虑在子组件data中定义一个变量,然后把props中接收得到的变量赋值给data,这样我们所有的函数就都可以写到子组件中了。
子组件参考:
<template> <div> <p>{{msg}}</p> <!-- <p>{{number}}</p> --> <p>{{value}}</p> <button @click="change">number+1</button> <div v-for="(item,index) in list" :key="index"> <p @click="changeItemInfo(index)">{{item.a}}</p> <p>{{item.b}}</p> <p>{{item.c}}</p> <p>{{item.d}}</p> </div> </div> </template> <script> export default { //model: { // prop: "number", //变量名 // event: "aa" //自定义的事件名 //}, props: { msg: String, value: Number, //接收number contentList: Array }, data() { list: []; }, watch: { contentList: { immediate: true, handler(newVal, oldVal) { if (newVal) { this.list = this.contentList; } } } }, methods: { change() { // this.$emit("aa", this.number + 1); this.$emit("input", this.value + 1); //必须使用默认的input事件进行变量更改提交 }, changeItemInfo(index) { this.$set(this.list[index], "a", "aaa"); //建议使用this.$set()提交数组或对象中的更改,否则嵌套比较深的数据更改了,视图有可能无法更新 } } }; </script>