文章目录
前言
当传递数据类型为引用数据类型:数组、方法、对象时,传递的是它们的引用地址
,接收到的组件同样可以对其堆区数据进行操作
。
父组件传递给子组件的,实际上只是一个
引用地址
,当子组件修改
这个对象时,是真的修改了在堆空间中保存的数值
,当然父组件中的值也会发生变化
,但是引用地址没有进行修改
,所以并没有报错。
注意:基本数据类型数据存放在栈区;引用数据类型地址名存放在栈区,数据存放在堆区。
组件之间数据传输。
template中用div包裹html元素是因为,template中只能包裹一个盒子。
1 父传子
通过属性值props接收
父传子:自定义属性 + props
props用于接收来自父组件的数据。
父传子:1)把数据当成子的自定义属性 2)在子对象中接收。
子组件需要在父组件中注册。
<template>
<div>
<son :str="str" ></son>
</div>
</template>
<script>
import Son from "./Son";
export default {
name:'Father'
data() {
return {
str: "给子组件传值"
}
},
components:{
Son// 把Son组件对象注册到了Father组件中
}
}
</script>
//子组件接收后,可以当作自身data用
<script>
export default {
name:'Son'
props:{
abc:String, //写法1:期待父给我传递的类型是String类型
title:{ //写法2:
type:String, // 期待父给我传递的类型是String类型
// required:true // 父要使用子组件,必须要传title 不传就报错
default:"我是一个默认值" //如果没传title指定默认值
},
}
props:["title","abc"]//写法3:传啥 要啥 不在乎数据类型
}
</script>
2 子传父
通过事件传值 $emit
子传父:自定义事件 + $emit
同样需要在父组件中注册。
<template id="father">
<div>
<h3>Father组件-------------{{fatherData}}------{{res}}</h3>
<!-- @click 不是点击事件 -->
<Son @clic="g"></Son>
<!-- 不一定是点击事件 只是个名字 除非写了点击方法 -->
<!-- @click自定义事件 事件名是click 点击时不会发生 自定义事件想让它发生,必须写代码-->
<!-- 如果这个自定义事件发生,会调用g -->
</div>
</template>
let Son = {
template:"#son",
// 给son模板中提供方法的
data(){
return{
msg:"son中的数据"
}
},
methods:{
k(){
// 写代码 触发Son身上的自定义事件
this.$emit("click",this.msg)//当前组件实例 前者是事件,后者是数据
}
}
}
// 1)定义Father组件
let Father = {
template:"#father",
data(){
return{
fatherData:"Father组件中的数据",
res:""
}
},
// 给模板提供方法
methods:{
g(value){
console.log("g....",value);
this.res = value;
}
},
// 把Son组件对象注册到了Father组件中
components:{
Son
}
}
3 处理边界情况(组件通信方案)
以下内容子组件据需注册到父组件中。
<div id="app">
<h1>{{msg}}</h1>
<hr>
<compa></compa>
</div>
3.1 $root(获得根组件实例)
在每个 new Vue 实例的子组件(在根实例中注册的)中,其根实例可以通过 $root property 进行访问。
this.$root 可以得到根组件实例
let compa = {
template: `
<h1>compa组件------<button @click="fn">点我</button></h1>
`,
methods: {
fn() {
// 在一个子组件中,可以通过$root,来获取根组件实例
console.log(this.$root);
console.log(this.$root.msg);
this.$root.f1()
// 表示修改根组件中的数据
this.$root.msg = "hi vue"
}
}
}
var vm = new Vue({
el: '#app',
data: {
msg: "hello vue"
},
methods: {
f1() {
console.log("f1.....");
}
},
components: {
compa
},
})
3.2 $parent(获得父组件实例)
this.$parent 得到父组件实例
console.log(this.$parent);
console.log(this.$parent.a);
this.$parent.gn()
3.3 $children(获得子组件实例)
this.$children 得到子组件实例
得到的是一个数组,原因是子可以有多个。
console.log(this.$children);
console.log(this.$children[0]);
console.log(this.$children[0].a);
this.$children[0].kn()
3.4 $refs(获得子组件实例)
this.$refs.xxx获得ref为xxx
的子组件实例
如果把ref写在普通的标签上,和id
一样,是为了获取这个DOM元素
<input type="text" ref="ipt" value="123">
如果把ref写在一个组件上,是为了获取这个组件实例(赋予ID引用)
<compa ref="a"></compa>
这样在任意地方通过this.$refs.xxx获得ref为xxx
的子组件的实例
mounted(){
// 得到a组件的实例
console.log(this.$refs.a);
console.log(this.$refs.a.number);
this.$refs.a.kn()
console.log(this.$refs.b.value);
},
3.5 provide & inject依赖注入
作用:可实现跨组件传值,数据的流只能是向下传递
provide : 必须在分级组件(不一定是app.vue)进行使用,用来给后代组件注入依赖(属性或方法)。
inject : 必须在子组件进行使用,用来获取根组件定义的跨组件传递的数据。
provide在父中提供数据,在子子孙孙组件中都可以使用数据。
可以实现跨级传输数据
var app = new Vue({
el: '#app',
data: {
username:"wangcai"
},
methods:{
},
// 在父中提供数据,在子子孙孙中可以消费数据
provide(){ //作为方法使用
return {
username:this.username
}
},
// provide:{ //作为对象使用
// username:this.username
// },
components:{
compa
}
})
子或孙组件:
let compb = {
inject:["username"], //接收数组
// inject:{ //接收对象
// username:{
// default:'默认值' //指定默认值
// }
// },
template:`
<h1>compb组件----------------{{username}}</h1>
`,
}
3.6 $attrs
当一个组件中没有声明任何 prop 时,
this.$attrs
可以获取到所有父作用域的属性绑定 (class 和 style 除外),并且可以通过v-bind="$attrs"
传给其内部组件 —— 在创建高级别的组件时非常有用。
props:["a","b","c"],
$attrs props可以接收数据,
$attrs可以接收所有的数据
并没有使用props接收a b c 但是$attrs中已经接收完毕
<son :a="1" :b="2" :c="3"></son>
let grandSon = {
template:`
<div>--------- grandSon组件----{{data}}-----</div>
`,
props:["data"],
}
let son = { //$attrs方法
template:`
<div>--------- Son组件 ---------<grandSon :data="$attrs" /></div>
`,
// template:` props方法
// <div>--------- Son组件 ---------<grandSon :a="a" :b="b" :c="c" /></div>
// `,
components:{
grandSon
}
}
3.7 $listeners
v-on:click="xx"
绑定点击事件
v-on=$listeners
绑定多个自定义事件
<!-- @f1="f1" @f2="f2" @f3="f3" 在son组件上绑定三个自定义事件 -->
<son @f1="f1" @f2="f2" @f3="f3"></son>
let grandSon = {
template:`
<div>--------- grandSon组件----<button @click="kn">触发</button>-----</div>
`,
// props:["fn"],
methods:{
kn(){
// this.fn.f1()
this.$listeners.f3()//接收 或者触发方法
}
}
}
let son = {// <grandSon v-on="$listeners" /> v-on="$listeners"在grandSon组件上绑定了三个自定义事件
template:`
<div>--------- Son组件 ---------<grandSon v-on="$listeners" /></div>
//注释<div>--------- Son组件 ---------<grandSon :fn="$listeners" /></div>
`,
mounted(){
// console.log(this.$listeners);
},
components:{
grandSon
}
}
3.8 event-bus(时间总线)
事件总线:
new一个Vue,可以创建一个根实例,也可以创建一条事件总线。
在事件总线上有一个机制,叫发布订阅:
发布:在合适的时机就可以发布 $emit()
订阅:订阅某个公众号 $on()
其实事件绑定也是发布订阅:
<button onclick="alert(6666)">登录</button>
事件绑定:订阅
当点击按钮时:发布
// 创建事件总线 使用一个空的 Vue 实例作为中央事件总线
let eventBus = new Vue();
========根组件中发布meassge
methods:{
fn(){
eventBus.$emit("meassge","hello 事件总线");
}
},
========其他组件中订阅meassge
mounted(){//生命周期中获取
eventBus.$on("meassge",function(msg){
console.log("message这个事发生了...",msg);
})
},
3.9 sync(修饰props)
sync对 props 起到了一种修饰的作用,使用 .sync 进行修饰的 props 意味子组件有修改它的意图,这种情况下它只起到一个标注性作用,有它没它都不会影响逻辑。
:xxx="xxx" @update:xxx="fn" 可以写成:xxx.sync="xxx"
,减少代码量
同时也不用在根实例methods中写
<body>
<div id="app">
<!-- @update:username="fn" 自定义事件 -->
<!-- 事件名是:update:username -->
<!-- <custom-input :username="username" @update:username="fn"></custom-input> -->
<!-- 修饰符sync 可以对上面的代码进行简写 -->
<custom-input :username.sync="username" ></custom-input>
<hr>
<h1>{{username}}</h1>
</div>
<script>
Vue.component("custom-input",{
template:`
<input type="text" :value="username" @change="$emit('update:username',$event.target.value)" />
`,
props:["username"]
})
var app = new Vue({
el: '#app',
data: {
username:"wangcai"
},
methods:{
// fn(msg){
// this.username = msg;
// }
}
})
</script>
</body>
文章参考:Vue 组件通信 12 种解决方案