前言 在 Vue 项目中父子组件的通讯是很是常见的,最近作项目的时候发现对这方面的知识还不怎么熟练,在这边作一下笔记,系统学习一下吧。html
父组件传值给子组件,这个就比较常见了,直接用 props 就能够了。可是就算是 props 子组件那边也有三种写法,以下面代码所示:
父组件vue
<!-- 两种状况 --> <!--静态传值--> <child name="xhm"></child> <!--动态传值--> <child :name="userName"></child>
子组件数组
// 1 简单粗暴就给个名称的状况 props:['name'], // 2 给个名称顺便指定个类型,若是父组件传递过来的值类型不对的话就会报错 props:{ name:String }, // 3 给个名称不只指定了类型,还顺便送了个默认值,当父组件传个空过来或者啥都没传过来的时候就用默认值了 props: { name: { type: String, default: 'xhm', } },
注意一下的话,若是是数组或者是对象要默认值的话,直接设置默认数组或者默认对象会报错,须要用工厂函数返回,以下:浏览器
props: { arr:{ type:Array, default:()=>{ return [1,2,3] } } }, // 对象也是和上面一个用工厂函数
因为单向数据流的限制,咱们不能直接在子组件中修改 props 的值,当咱们修改的时候会报错,官方的说法是:函数
全部的 prop 都使得其父子 prop 之间造成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,可是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而致使你的应用的数据流向难以理解。额外的,每次父级组件发生更新时,子组件中全部的 prop 都将会刷新为最新的值。这意味着你不该该在一个子组件内部改变 prop。若是你这样作了,Vue 会在浏览器的控制台中发出警告。学习
因此啊,若是你不仅是想在子组件中简单的渲染父组件传过来的值的话,那么能够用下面的两种方法。this
这个 prop 用来传递一个初始值;
这个子组件接下来但愿将其做为一个本地的 prop 数据来使用。在这种状况下,最好定义一个本地的 data 属性并将这个 prop 用做其初始值:props: ['name'], data() { return { userName:this.name, }; },
这个 prop 以一种原始的值传入且须要进行转换。
在这种状况下,最好使用这个 prop 的值来定义一个计算属性:props: ['name'], computed: { userName(){ return this.firstName + this.name } },
虽然咱们说是要单向数据流,可是不少时候,咱们在子组件改变了某些值以后,仍是要反馈给父组件,让父组件作一下修改,那么这个时候就要想着子组件向父组件传值啦。有下面这么两种方式双向绑定
这个基本都是用 emit 来传递了,用法直接看代码吧:
子组件code
// props:['name] // methods 里 update(){ this.$emit('update','ljy'); // 第一个参数:事件名,第二个参数:传递给事件方法的参数 }
父组件htm
/* template 里面的代码,监听子组件里面的 update 事件,调用父组件的 childUpdate 方法 <child :name="userName" @update="childUpdate"></child> */ // methods chidlUpdate(val){ // val 参数就是子组件传递过来的数据 this.userName = val; }
虽然这样是能够实现子组件向父组件传值,可是写多一个方法感受很烦,因此 vue 官方高出了一个 以 update:myPropName 的模式触发事件。
,这个是啥,举个例子,咱们的子组件中有一个 name
的 props,咱们用下面这个形式通知父组件
this.$emit('update:name', newName) // this.$emit('update:props中的变量名', 新的值)
而后父组件能够监听那个事件并根据须要更新一个本地的数据属性:
<child :name="userName" @update:name="userName = $event"></child>
这样当咱们在子组件触发那个修改的方法的时候,父组件的 userName
变量就会更新为 newName
了,而后为了方便起见,官方提供了一个缩写,即 .sync
修饰符。看代码吧:
<child :name.sync="userName"></child>
上面的代码和前面的代码是一个效果,是否是方便了不少。舒服了吧。
这个的话是针对于 对象和数组那些引用类型的数据而言的,因为这些存在浅拷贝的问题(不明白浅拷贝的看这篇文章),因此能够利用这点来实现子父组件的「同生共死」,看代码吧
// 假设 name 是一个对象或者数组 props:['name'], return { userName:this.name, };
就这样?!
没错就是这么简单粗暴,因为浅拷贝的问题,咱们在子组件修改 userName
的时候,从父组件传递过来的那个值也会改变的,而后就会实现 props 的「双向绑定」了。可是通常没人会这么干,由于这样会形成维护上的问题,会让人以为咋没干啥父组件的值咋就变了,会让人头秃啊,因此除非你项目中非得要搞这么一个子父组件 props 的「同生共死」,那就这么干吧。
不知道这样的叫法对不对,反正就这样啦。总结以后又下面这几种方法
其实原本 emit 就是用于子组件向父组件通讯的,上面的子组件传值给父组件其实也就是父组件监听子组件的事件,而后触发父组件的方法的,换个说法,也就是子组件调用了父组件的方法,再写一下代码吧:
子组件
// methods 里 update(){ this.$emit('update','ljy'); // 第一个参数:事件名,第二个参数:传递给事件方法的参数 }
父组件
/* template 里面的代码,监听子组件里面的 update 事件,调用父组件的 childUpdate 方法 <child :name="userName" @update="childUpdate"></child> */ // methods chidlUpdate(val){ // val 参数就是子组件传递过来的数据 this.userName = val; }
上面的代码中,从某种意义上来讲,就是子组件调用了父组件的 childUpdate
方法。
这个就比较简单了,咱们假设咱们在父组件定义了一个 fatherMethod()
方法,而后咱们子组件就能够经过下面的代码实现调用 fatherMethod()
的方法
childClick(){ this.$parent.fatherMethod(); }
props 能传递 Function 类型的数据,因此,咱们经过 props 固然也是能够直接的调用父组件传递过来的方法啦。很少说,直接撸代码:
父组件
<!-- 假设父组件里定义了一个 fatherMethod() 方法 --> <child :fatherMethod="fatherMethod"></child>
子组件
props: { fatherMethod: { type: Function, default: null } }, methods: { childClick() { this.fatherMethod(); } },
这样咱们也是调用了父组件的方法啦。
这个,暂时没有遇到过这种状况,不过以备不时之需,也写一下吧。父组件调用子组件的方法的话是利用 ref 获取到子组件实例,从而调用子组件的方法,假设咱们子组件有这么一个 childMethod()
方法。那么咱们的父组件就能够这么来调用子组件的方法了
/* <child ref="con"></child> 子组件 */ methods: { update() { this.$refs.con.childMethod(); }, }
至此,关于父子组件通讯的的话题就聊到这边了,若是有啥错误或者遗漏的,欢迎在下面斧正啦。