一起来学习Vue的组件间通信方式
props
父组件可以通过props向下传递数据给子组件
静态的Props
通过为子组件在父组件中的占位符添加特性的方式来达到传值的目的
动态Props
在模版中要动态的绑定父组件的数据到子模版的props,与绑定任何普通的HTML特性相类似,用v-bind,每当父组件的数据发生变化的时候,该变化会传导给子组件
props验证
可以为组件的props指定验证规则,如果传入的数据不符合规则,Vue会发出警告
props: { // 基础类型检测 (`null` 意思是任何类型都可以) propA: Number, // 多种类型 propB: [String, Number], // 必传且是字符串 propC: { type: String, required: true }, // 数字,有默认值 propD: { type: Number, default: 100 }, // 数组/对象的默认值应当由一个工厂函数返回 propE: { type: Object, default: function () { return { message: 'hello' } } }, // 自定义验证函数 propF: { validator: function (value) { return value > 10 } } }
type 可以是(String
,Number
,Boolean
,Function
,Object
,Array
,Symbol
)原生构造器
type也可以是自定义构造函数,使用instenceof检测
prop是当先绑定的,当父组件的属性变化是,将传导给子组件,但是不会反过来,这是为了防止子组件无意修改了父组件的状态。
修改prop数据
- prop 作为初始值传入后,子组件想把他当作局部数据来用
- prop 作为初始值传入,有子组件处理成其他数据输出
- 注意:js中对象和数组是引用类型,指向同一个内存空间,如果prop是一个对象或数组,在子组件内部改变它会影响父组件的状态
- 处理方法1:这个时候需要定义一个局部变量,并用prop初始化它,但是局部变量只能接受prop初始值,当父组件的值发生变化的时候,无法接收到最新值
- 处理方法2:定义一个计算属性,处理prop值并返回
- 初始方法3:使用变量存储prop的初始值,并使用watch来观察prop的值的变化,当发生变化的时候更新变量的值
$emit
$emit(‘自定义时间名’,要传送的数据)触发当前实例上的事件
父组件
<template> <children @addCount="addCount" :count="count"/> <template> <script> import children from "./children"; export default { name:'index', components: {Children}, data () { return { count:0 } }, methods:{ addCount(data){ this.count = data.count; } } } </script>
子组件
<template> <div> <h3>计数器:{{count}}</h3> <button @click='add'>+++</button> </div> </template> <script> export default { name:'children', props:['count'], // 用来接收父组件传给子组件的数据 methods:{ add() { // 触发父组件的方法 this.$emit('addCount',{count: count + 1}); } } } </script>
$on
$on(‘事件名’,callback)监听事件,监听当前实例上的自定义事件
父组件
<template> <div> <span>{{count}}</span> <children/> </div> <template> <script> import { bus } from '../main.js'; import children from "./children"; export default { name:'index', components: {Children}, data () { return { count:0 } }, mounted(){ bus.$on('addCount',(val)=>{ this.count++; }) } } </script>
子组件
<template> <button @click='add'>count+++</button> </template> <script> import { bus } from '../main.js'; export default { name:'children', methods:{ add() { // 触发父组件的方法 bus.$emit('addCount',{}); } } } </script>
main文件
export var bus = new Vue();
注意: e m i t 和 emit和 emit和on的事件必须在一个公共的实例上, e m i t 触 发 的 事 件 emit触发的事件 emit触发的事件on才能监听到。
$parent(不常用)
指向当前组件树的根实例,如果当前实例没有父实例,则实例将会指向自己。
- $parent既可以接受父组件数据,又可以修改父组件数据,是双向的。
- $parent可以调用父组件的方法
子组件
<template> <button @click='actionParent'>触发父组件发方法</button> </template> <script> export default { name:'children', methods:{ actionParent() { console.log(this.$parent.name); this.$parent.parentPrint(); // 触发父组件的方法 } } } </script>
$children(不常用)
指向当前实例的直接子组件,返回的是一个组件的集合。
需要注意: children 并 不 保 证 顺 序 , 也 不 是 响 应 式 的 。 如 果 你 发 现 自 己 正 在 尝 试 使 用 children并不保证顺序,也不是响应式的。如果你发现自己正在尝试使用 children并不保证顺序,也不是响应式的。如果你发现自己正在尝试使用children进行数据绑定,考虑使用一个数组配合v-for来生成子组件,并且使用Array作为真正的来源。
for(let i=0; i<this.$children.length; i++){ console.log(this.$children[i].name); // 输出子组件的name }
$attrs
包含了父作用域中不作为prop被识别(且获取)的特性绑定(class,style除外)。当一个组件没有声明任何prop时,这里会包含所有父作用域的绑定,并且可以通过v-bind="$attrs"传入内部组件–在创建高级别的组件时非常有用。
简单说:接收除了props声明外的所有绑定属性(calss,style除外)
上图的$attrs中只有age,gender两个属性{ age: “20”, gender: “man” }
在grandson上通过v-bind="$attrs",可以将属性继续向下传递,让grendson也能访问到父组件的属性。
这种方式当传递多个属性时会显得很便捷,不需要一条一条进行绑定。
如果想要添加其他属性,可继续绑定属性,注意:继续绑定的属性和$attr中的属性有重复时,继续绑定的属性优先级会更高
$listeners
包含了父级作用域中的(不含.native修饰器的)v-on事件监听器,他可以通过v-on="$listeners"传入内部组件–在创建更高层次的组件时非常有用。
简单说:接收除了带有.native事件修饰符的所有事件监听器
child组件绑定了带有.native的click事件和一个自定义事件,$listeners输出的结果为:{customEvent:fn}
通过v-on=" listeners"将事件监听器继续向下传递,让grandson访问到事件。并且可以使用emit触发传递下来的事件。
如果想要添加其他事件监听器,可以继续绑定事件
注意:继续绑定的事件和$listeners中的事件有重复的时候,不会被覆盖。当grandson触发customEvent时,child和parent的事件都会被触发,触发顺序类似于冒泡。
使用场景
- 组件传值时使用,父亲,儿子,孙子三级传递参数交互。
- 对一些UI库进行二次封装的时候使用,比如element-ui中的组件不能满足自己的使用场景时,会二次封装,但是又想保留它自己的属性和方法,这个时候使用。
$refs
this.$refs是一个对象,持有当前组件中注册过ref特性的所有DOM元素和子组件实例
注意:$refs只有在组件渲染完成之后才填充,在初始渲染的时候不能访问他们,并且它是非响应式的,因此不能用它在模版中做数据绑定。
父组件
<template> <div> <div ref="testDom">123</div> <child ref="child" /> <button @click="openChild" >触发子组件</button> </div> </template> <script> import Child form './child.vue' export default { components:{ Child }, mounted(){ console.log(this.$refs.testDom) // <div>123</div> console.log(this.$refs.child.name) // 粉刷匠 }, methods:{ openChild(){ this.$refs.child.open(); } } } </script>
子组件
<template> <div>{{name}}</div> </template> <script> export default { data(){ return { name:"粉刷匠" } }, methods:{ open(){ alter("点击了") } } } </script>
注意:当ref和v-for一起使用的时候,获取到的将会是一个数组,包含循环数组源。
provide && inject
provide/inject是vue2.2版本之后新增的高级组件,这两个组件要一起使用。
允许一个祖先组件向其所有的子孙后代注入一个依赖。无论组件层次有多深,并在其上下游关系成立的事件里始终有效。类似于React的上下文。
provide选项是一个对象 或返回一个对象的函数。该对象包含可注入其子孙的property。
inject有下面两种:
- 一个字符串数组
- 一个对象
- 对象的key是本地的绑定名,
- value
- 在可用的注入内容中搜索用的key(字符串/Symbol)
- 或一个对象
- from property是在可用的注入内容中搜索用的key(字符串/Symbol)
- default property是降级情况下使用的value
provide和inject绑定并不是可响应的,这是vue官方刻意为之的。如果你传入一个可监听的对象,那么其对象的property还是可响应的。
缺点:在任意层级都能访问导致数据追踪比较难,所以provide/inject能不使用就不使用,尽量使用vuex。在组件库开发的可以使用
副组件
<template> <div class="test"> <son prop="data"></son> </div> </template> <script> export default { name: 'Father', provide: { chontrol: this } data(){ return { name:"啊哈哈" } } }
// 某一级子组件/孙子组件
<template> <div> {{name}} </div> </template> <script> export default { name: 'son', inject: ["chontrol"] props: { name: { type: Object, default: () => ({}), }, } }, } </script>
总结
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!