[A] 父子间的通信
1. 我们知道,子组件是不能直接引用父组件或者Vue实例中的数据的
2. 但是在实际开发中,我们经常需要数据从上层网下层传递
如:在一个页面中,主页面从服务器请求到了很多数据
其中一部分在大组件中展示,一部分在小组件中展示
这个时候就需要数据通过大组件(父组件)传递给小组件(子组件)
[B] 父组件的通信方法
步骤:
1. 创建子组件,并添加props属性用于接收父组件传过来的数据
2. 在vue实例中使用子组件时,在行内通过v-bind绑定父组件中需要传递的数据
3. 在子组件模板中即可使用父组件中传过来的数据
// 1. 创建组件 const cpn1 = { template: ` <div> <h2>我是组件{{fruits}}</h2> <div>{{fruits}}的内容: {{types}}</div> </div> `, props:["fruits", "types"], } // 2. 使用组件 var app = new Vue({ el: "#app", data:{ f: "水果", t: ["西瓜", "椰子", "柚子", "苹果"] }, components:{ cpnc1: cpn1, } }); // 3. html中调用组件 <div id="app"> <cpnc1 :fruits="f" :types="t"></cpnc1> </div>
props传值要点:
1. props属性可以是数组,也可以是对象
2. props为数组时,数组元素应为字符串,
3. props为对象时,可以添加很多限制条件
props:{
fruits:{
type: String, // 限定该变量必须为字符串
default: "默认值", // 设置默认值,当未传值时,使用该值
required: true, // 是否为必传值,为true表示必须传该值
}
}
4. props中的数据是父组件传过来的,是不允许手动修改的(修改会成功,但是会有报错信息)。
如果想要手动修改props中的数据,需要在data属性中修改(data中新建一个变量,其值等于props中的变量).
实际上,这并没有修改props中的值,若要修改,可将数据线发送给父组件,然后通过父组件修改数据后将值传过来
一句话,要修改子组件props的值,就要想办法让父组件来修改。
【注意点】:
1. 当默认值为数组或者是对象时,必须写成函数,函数返回值即为默认值。
2. 也可自行验证传入值的类型
props:{
fruits:{
validator: function(value){
// 返回true则表示验证成功
return ["apple", "orange", "watermelon"].indexof(value) !== -1;
}
default: "默认值", // 设置默认值,当未传值时,使用该值
required: true, // 是否为必传值,为true表示必须传该值
}
}
方法二:通过$children访问
组件的this.$children属性返回的是该组件所有的子组件数组
注意:这里的子组件指的是在父组件模板中使用的子组件,同一个子组件使用多次,$children就返回多个子组件组成的数组
步骤:
1. 在父组件模板中调用子组件
2. 在父组件中即可通过 this.$children[k].变量名 调用第k个子组件的指定变量
除了上述方法,this.$children.$data对象中保存了所有的变量,也可以通过这种方法调用。
// 模板 <div id="app"> <cpnc></cpnc> <cpnc></cpnc> <cpnc></cpnc> <button @click="cpnClick">按钮</button> </div> // 1. 创建子组件 const cpn = { template: ` <div> <h2>计数器:{{counter}}</h2> <button @click="minusCounter()">minus</button> <button @click="addCounter()">add</button> </div> `, data(){ return { counter: 0, } }, methods:{ addCounter(){ this.counter++; }, minusCounter(){ this.counter--; } }, } // 2, 3. 使用组件 var app = new Vue({ el: "#app", components:{ cpnc: cpn, }, methods:{ cpnClick(){ var comp = this.$children; for(var s of comp){ console.log(s.counter); } } }, });
方法三:通过$refs访问
在上述的$children方法中存在一个问题,即$children返回的是所有的子组件,但会我们需要返回某个指定的组件就比较麻烦
组件的this.$refs属性返回的是所有有ref属性的子组件组成的一个对象
ref通信
1. ref属性放在标签上(行内属性位置),拿到的是原生节点
2. ref属性放在组件上,拿到的是组件对象
步骤:
1. 给需要调用其数据的子组件添加ref="aaa"的行内属性
2. 在父组件中调用this.$refs.aaa属性即可获得上面所标记的子组件
示例代码:
// 模板 <div id="app"> <cpnc ref="ref1"></cpnc> <cpnc></cpnc> <cpnc ref="ref3"></cpnc> <button @click="cpnClick">按钮</button> </div> // 1. 创建子组件 const cpn = { template: ` <div> <h2>计数器:{{counter}}</h2> <button @click="minusCounter()">minus</button> <button @click="addCounter()">add</button> </div> `, data(){ return { counter: 0, } }, methods:{ addCounter(){ this.counter++; }, minusCounter(){ this.counter--; } }, } // 2, 3. 使用组件 var app = new Vue({ el: "#app", components:{ cpnc: cpn, }, methods:{ cpnClick(){ var comp = this.$refs; var keys = Object.keys(comp); // ["ref1", "ref3"] for(var s of keys){ console.log(comp[s].counter); } } }, });
子传父:
方法一:自定义事件,子组件通过$emit发送给自定义事件,父组件通过v-on监听自定义事件
步骤:
1. 子组件模板中添加事件,在事件中通过this.$emit("cpnClick", v1)发送自定义事件(即cpnClick事件)。
2. 在父组件中通过v-on监听这个自定义事件,@cpnClick="getMsg(ins)", 在自定义事件函数中接收发过来的数据getMsg(ins){}
3. BingO
方法二:通过$parent访问父组件
步骤:
1. 子组件中通过this.$parent调用父组件,进而可以使用父组件的数据
2. 注意,this,$parent只能访问父组件,不能访问祖先组件
3. 若要访问根组件(即Vue实例),需要调用this.$root来访问
注意:该方法会造成组件和他的父组件耦合性很高,不利于组件的复用,故很少用
示例代码:
// 父组件模板 <div id="app"> <pcpn></pcpn> <pcpn></pcpn> <pcpn></pcpn> <button @click="cpnClick()">按钮</button> </div> // 子组件模板 <template id="cpntem"> <div> <h2>我是{{name}}</h2> <p>你们好,我是小白,以后请多关照</p> <button @click="btnClick()">按钮</button> </div> </template> // 1. 创建子组件 const cpn = { template: "#cpntem", data(){ return { name: "Carrey" } }, methods:{ btnClick(){ console.log(this.$parent.num); } }, } // 2, 3. 使用组件 var app = new Vue({ el: "#app", data:{ num: 120, }, components:{ pcpn: cpn, }, methods:{ cpnClick(){ console.log("---------------"); } } });
有时候父子组件之间需要通信,兄弟组件之间也需要通信,不同层级的组件之间都有可能需要通信
这时需要一个独立于各个层级组件之外的公共平台来实现交流便可大大提高效率,简化代码
事件总线就是一个游离于所有组件之外的一个公共平台,实际上是一个空的vue实例对象
任何组件都可以像这个平台发送数据,任何组件也都可以监听并接受这个平台的数据
事件总线原理:
1. 在云端(事件总线上)有一个平台,该平台可以接受任何组件发来的数据,这个平台上的
数据也可以被任何组件所监听和获取
2. 发送方组件,将需要发送的数据像云端发送,并以指定的名字保存在云端
3. 接收方组件时刻监听着云端某个名字的数据,一旦监听到该数据,就响应并进行后续数据处理
案例:作者在平台上发布文章,用户读者从平台上获取文章并显示在自己的桌面上
<body> <div id="app"> <cpn_1></cpn_1> ************************* <cpn_2></cpn_2> </div> <script> // 1. 创建云端平台(即数据总线) var cloud = new Vue(); // 2. 创建全局组件1(该组件是文章作者) Vue.component("cpn_1",{ template:` <div> 我是平台作者,我可以发布信息<vr. <input type="text" ref="mytext"> <button @click="send()">发送</button> // a. 作者可以启动发送程序 </div> `, methods:{ send:function(){ cloud.$emit("xxx", this.$refs.mytext.value); // b. 在发送程序里,指定发送的数据为: this.$refs.mytext.value 数据在云台上的名字为: xxx } } }) // 3. 创建全局组件2(该组件是用户读者) Vue.component("cpn_2",{ template:` <div> 我是移动端用户,我需要查看信息<br> <textarea cols="30" rows="10" ref="get_area"></textarea> </div> `, mounted(){ // c. 用户读者时刻监听者云台上名叫xxx的数据是否存在 cloud.$on("xxx", (data)=>{ this.$refs.get_area.value = data; // d. 监听到数据之后,将监听到的数据获取,并展示在自己的桌面上 }) } }) // 4. Vue实例化 new Vue({ el: "#app", data(){ return{ name: "jack", age: 20 } }, }) </script> </body>
【补充】mounted属性是组件中的一个常规属性,与el, data, methods, components等属性同级,mounted是一个在组件船舰好之后,首先运行的属性