看完这篇文章你就会组件通信啦

1. 父传子

从上向下传值,将父级的数据传递给子级,在子级当中可以使用父级的值

1.1. 使用props传值

创建父组件

<template>
    <div>
      <h1>我是父组件</h1>
      <!-- 3.使用子组件 -->
      <Child />
    </div>
</template>
<script>
  // 1.导入子组件
import Child from './child.vue'
export default {
  components:{
    // 2.注册子组件
    Child
  },
  data(){
    return{
      // 这里是父组件中的数据
      msg:'我是父组件中的数据'
    }
  }
}
</script>

创建子组件

 <!-- child.vue -->
<template>
    <div>子组件</div>
</template>

现在将父组件中的数据传递给子组件,可以在子组件<Child />上添加一个自定义属性,将父组件的数据作为value值进行传递,子组件中使用props进行接收

父组件:

<template>
    <div>
      <h1>我是父组件</h1>
      <!-- 3.使用子组件 -->
      <Child :title='msg' />
    </div>
</template>
<script>
  // 1.导入子组件
import Child from './child.vue'
export default {
  components:{
    // 2.注册子组件
    Child
  },
  data(){
    return{
      // 这里是父组件中的数据
      msg:'我是父组件中的数据'
    }
  }
}
</script>

子组件:

 <!-- child.vue -->
<template>
  <!-- 这样在组件标签中就可以使用了 -->
    <div>子组件</div>
    <p>这是从父组件传递过来的数据:<i>{{ title }}</i></p>
</template>
<script>
export default {
  props: ['title'] // 这里的title就是我们再子组件标签上自定义的key
}
</script>

注意:props接收的数据是只读的,不可以被修改,所有修改都会失效和被警告

1.2. 使用ref

父组件:

<template>
  <div class="parent">
    父组件
    <!-- 1.在子组件上使用ref属性,给组件起一个名字 -->
    <Child ref="child" />
    <button @click="send">传递数据给子组件</button>
  </div>
</template>
<script>
import Child from './child.vue'
export default {
  components: {
    Child
  },
  data() {
    return {
      msg: '我是父组件中的数据'
    }
  },
  methods: {
    send() {
      // 2.通过this.$refs.组件标签上的ref属性值.子组件中的方法
      this.$refs.child.childFn(this.msg)
    }
  }
}

</script>
<style>
.parent {
  width: 420px;
  background: orange;
  padding: 30px;
}

</style>

子组件:

<template>
  <div class="child">
    child子组件
  </div>
</template>
<script>
export default {
  methods: {
    // 3.在子组件上定义一个方法,这个方法需要有一个参数来接收父组件传递过来的数据
    childFn(receiveData) {
      console.log('我是父组件传递过来的数据:', receiveData)
    }
  }
}
</script>
<style>
.child {

  padding: 30px;
  background: pink;
}

</style>

2. 子传父

2.1. 通过props结合回调函数参数实现通信

通过父组件给子组件传递函数类型的props实现子传父

父组件:

<template>
	<div class="parent">
		<p>parent组件</p>
		<Child :getSchoolName="getSchoolName" />
	</div>
</template>
<script>
import Child from './child.vue'
export default {
	components: {
		Child
	},
	methods: {
    // 这里的val相当于形参,接收子组件传递过来的实参
		getSchoolName(val) {
			console.log('接收到了子组件传递来的数据:', val)
		}
	}
}
</script>
<style>
.parent {
	width: 420px;
	background: orange;
	padding: 30px;
	color: white;
}
</style>

子组件:

<template>
	<div class="child">
		<p>child组件</p>
		<button @click="sendSchoolName">传递数据给父组件</button>
	</div>

</template>
<script>
export default {
	props: ['getSchoolName'],
	methods: {
    // 在子组件中调用父组件传递过来的函数,将子组件中的数据作为实参传递给父组件。
		sendSchoolName() {
			this.getSchoolName('金小子')
		}
	}
}
</script>
<style>
.child {
	background: pink;
	padding: 30px;
}
</style>

2.2. 自定义事件,通过$emit()进行传递

从下向上传递,将子组件中的数据传递给父组件

子组件:

<template>
  <div>我是子组件
    <!-- 给子组件添加一个点击事件 -->
      <a href="JavaScript:;" @click="send">点我把子组件的数据传递给父组件</a>
  </div>
</template>
<script>
export default {
  data() {
      return {
          childData: '我是子组件的数据'
      }
  },
  methods: {
    // 通过$emit将数据传递给父组件,第一个参数是自定义的事件名,第二个参数是传递给父组件的数据
    send() {
      this.$emit('customEvent', this.childData)
    }
  }
}
</script>

$emit是Vue实例上的一个方法,他有两个参数

this.$emit(自定义的事件名称,要传递的数据)

父组件:

<template>
  <div>
      我是父组件
      <hr>
        <!-- 在父组件上绑定传递过来的自定义事件名($emit中的第一个参数),给这个事件绑定一个方法 -->
      <child @customEvent="ParentReceive"/>
  </div>
</template>
<script>
import Child from './child'
export default {
  components: {
      Child
  },
  methods: {
    // 在这个方法中设置一个参数,这个参数就是子组件的$emit传递过来的第二个参数
    ParentReceive(val) {
      console.log(val) // val就是子组件传递过来的数据了
    }
  }
}
</script>

2.3. 通过ref实现子传父??

父组件:

<template>
    <div class="parent">
        <p>parent组件:{{ msg }}</p>
        <!-- 使用子组件 -->
        <Child ref="child" />
    </div>
</template>
<script>
import Child from './child.vue'
export default {
    components: {
        Child
    },
    data() {
        return {
            msg: ''
        }
    },
    methods: {
        getChildName(val) {
            this.msg = val
        }
    },
    mounted() {
        this.$refs.child.$on('child', this.getChildName)
    }
}
</script>
<style>
.parent {
    width: 420px;
    background: orange;
    padding: 30px;
    color: white;
}
</style>

子组件:

<template>
  <div class="child">
    <p>child组件</p>
    <button @click="sendSchoolName">传递数据给父组件</button>
  </div>
</template>
<script>
export default {
  methods: {
    sendSchoolName() {
      // child是自定义的事件名,"金小子"就是要传递给父组件的参数
      this.$emit('child', '金小子')
    }
  }
}

</script>
<style>
.child {
  background: pink;
  padding: 30px;
}

</style>

3. $ref、$emit和props的区别

特性

refs

props

$emit

通信方向

父组件 → 子组件

父组件 → 子组件

子组件 → 父组件

数据流

单向(父操作子)

单向(父传子)

单向(子通知父)

数据类型

获取组件实例/DOM元素

任意数据(对象/数组等)

事件+数据

修改权限

父组件可直接修改子组件

子组件不能直接修改

父组件监听子组件事件

使用场景

需要直接操作子组件时

父传数据给子组件时

子组件通知父组件时

4. 自定义事件修饰符

4.1. $once,ref方式

父组件:

<template>
  <div class="parent">
    <p>parent组件</p>
    <Child ref="child" />
  </div>
</template>
<script>
import Child from './child.vue'
export default {
  components: {
    Child
  },
  methods: {
    getChildName(val) {
      console.log(val)
    }
  },
  mounted() {
   this.$refs.child.$once('child', this.getChildName)
  }
}

</script>

子组件:

<template>
    <div class="child">
        <p>child组件</p>
      <!-- 只能点击一次,点击多次无效 -->
        <button @click="sendSchoolName">传递数据给父组件</button>
    </div>
</template>
<script>
export default {
    methods: {
        sendSchoolName() {
            // child是自定义的事件名,"金小子"就是要传递给父组件的参数
            this.$emit('child', '金小子')
        }
    }
}

</script>
<style>
.child {
    background: pink;
    padding: 30px;
}
</style>

4.2. 解绑自定义事件$off

子组件:

<template>
  <div class="child">
    <p>child组件</p>
    <button @click="sendSchoolName">传递数据给父组件</button>
    <button @click="unbind">解绑自定义事件</button>
  </div>
</template>
<script>
export default {
  methods: {
    sendSchoolName() {
      this.$emit('child', '金小子')
    },
    unbind() {
    	this.$off('child') // 解绑事件
    }
  }
}

</script>
<style>
.child {
  background: pink;
  padding: 30px;
}

</style>

父组件:

<template>
  <div class="parent">
    <p>parent组件</p>
    <Child @child="receiveHandle"  />
  </div>
</template>
<script>
import Child from './child.vue'
export default {
  components: {
    Child
  },
  methods: {
    receiveHandle(val) {
    	console.log(val)
    }
  }
}

</script>
<style>
.parent {
  width: 420px;
  background: orange;
  padding: 30px;
  color: white;
}

</style>

4.2.1. 解绑多个自定义事件

this.$off(['自定义事件1', '自定义事件2'])

4.2.2. 解绑所有自定义事件

this.$off()

5. 兄弟组件通信

5.1. 有共同的父组件

子组件1:

<!-- child-a.vue -->
<template>
  <div>我是child-a组件
      <a href="JavaScript:;" @click="send">点击传送数据给我老弟</a>
  </div>
</template>
<script>
export default {
  data() {
      return {
          msg: '我是child-a组件中的数据'
      }
  },
  methods: {
      send() {
          this.$emit('sendB', this.msg)
      }
  }
}
</script>

父组件:

<template>
  <ChildA @sendB="receiveDataA" />
  <ChildB :title="value" />
</template>
<script>
  import ChildA from './child-a.vue'
  import ChildB from './child-b.vue'
  export default {
    data() {
        return {
            // 中转的变量
            value: ''
        }
    },
    methods: {
        receiveDataA(val) {
            console.log(val)
            this.value = val
        }
    },
    components:{
      ChildA,
      ChildB
    }
}
</script>

子组件2:


<!-- child-b.vue -->
<template>
  <div>我是child-b组件
      <p>这是child-a传递过来的数据:<i>{{ title }}</i></p>
  </div>
</template>
<script>
export default {
  props: ['title']
}
</script>

这里如果是同一个父组件,总结来说就是,先通过子传父,然后再通过父传子,进行传递

5.2. 无共同父组件的兄弟通信

  1. 创建组件

父组件:

<!-- parent.vue 这里的parent仅仅是为了页面结构不再作为中转了 -->
<template>
    <div>
        我是父组件
        <hr>
        <!-- 第三步:使用组件 -->
        <child-a />
        <child-b />
    </div>
</template>
<script>
// 第一步:导入组件
import childA from './child-a.vue'
import childB from './child-b.vue'

export default {
  // 第二步:组件注册
    components: {
        childA,
        childB
    }
}
</script>

子组件1:

<!-- child-a.vue -->
<template>
    <div>我是child-a组件
        <a href="JavaScript:;">点击传送数据给兄弟组件</a>
    </div>
</template>
<script>
export default {
    data() {
        return {
            msg: '我是child-a组件中的数据'
        }
    }
}
</script>

子组件2:

<!-- child-b.vue -->
<template>
    <div>我是child-b组件
        <span>这是child-a传递过来的数据:<i></i></span>
    </div>
</template>
  1. 在main.js中创建一个空的Vue实例,这个实例的目的充当的角色就是通信(理解为有共同父组件的parent组件)
// 创建空的vue实例
const eventBus = new Vue()
// 将创建出来的空的Vue实例挂载到Vue的构造函数的原型上(构造函数的原型上的方法或者属性在任何地方我们都已调用)
Vue.prototype.eventBus = eventBus

思路:child-a发送数据 -> child-b接收数据(这种兄弟组件通信方式和上一种相比不再借助于共同的父级组件了)

子组件1:

<!-- child-a.vue -->
<template>
    <div>我是child-a组件
      <!-- 
1. 在child-a组件中添加点击事件并且绑定方法,然后再方法中还是通过$emit完成数据的传递 -->
        <a href="JavaScript:;" @click="sendA">点击传送数据给我老弟</a>
    </div>
</template>
<script>
export default {
    data() {
        return {
            msg: '我是child-a组件中的数据'
        }
    },
    methods: {
        sendA() {
            // 注意这里的$emit是Vue空实例上面的不是Vue实例上面的
            this.eventBus.$emit('send', this.msg)
        }
    }
}
</script>

子组件2:

<!-- child-b.vue -->
<template>
    <div>我是child-b组件
        <span>这是child-a传递过来的数据:<i>{{ receiveData }}</i></span>
    </div>
</template>
<script>
export default {
    data() {
        return {
            receiveData: ''
        }
    },
    created() {
        this.eventBus.$on('send', (val) => {
            console.log(val)
            this.receiveData = val
        })
    }
}
</script>

更加优雅的写法,可以在new Vue实例的beforeCreate中给Vue实例原型上绑定一个属性然后指向当前的Vue实例

import Vue from 'vue' // 导入vue.js相当于
// import App from './App.vue' // 导入App.vue组件
import Parent from './components/parent.vue'

// 关闭开发环境的提示
Vue.config.productionTip = false
// Vue实例
new Vue({
  render: h => h(Parent), // 把App组件挂载到#app的html页面上
  beforeCreate() {
    Vue.prototype.eventBus = this
  }
}).$mount('#app')

销毁全局事件总线,如果组件被销毁了但是事件总线是全局的,依旧存在因此为了性能更优需要手动进行销毁

beforeDestroy() {
  this.eventBus.$off('自定义的事件名')
}

6. 组件绑定原生事件.native

如果我们在父组件中,给子组件添加点击事件,那么vue会认为click是一个自定义事件,而不会执行

<template>
	<div class="parent">
		<p>parent组件,msg的值是:{{ msg }}</p>
		<Child ref="child" @click="show" />
	</div>
</template>
<script>
import Child from './child.vue'
export default {
	components: {
		Child
	},
	data() {
		return {
			msg: ''
		}
	},
	methods: {
		show() {
			console.log('show')
		}
	},
	mounted() {
		this.$refs.child.$on('child', val => {
			console.log(this)
			this.msg = val
		})
	}
}
</script>

那么我们如果要给子组件添加原生事件,需要使用修饰符.native

<template>
	<div class="parent">
		<p>parent组件,msg的值是:{{ msg }}</p>
		<Child ref="child" @click.native="show" />
	</div>
</template>
<script>
import Child from './child.vue'
export default {
	components: {
		Child
	},
	data() {
		return {
			msg: ''
		}
	},
	methods: {
		show() {
			console.log('show')
		}
	},
	mounted() {
		this.$refs.child.$on('child', val => {
			this.msg = val
		})
	}
}
</script>

7. $children/$parent

$children:获取到一个包含所有子组件(不包含孙子组件)的实例对象数组,直接拿到子组件中所有数据和方法

$parent:获取到一个父组件的实例对象,同样包含父节点中所有数据和方法

<template>
    <div>
        <span style="font-weight: bold; color: red; font-size: 24px;">我是父组件:</span>获取到子组件中的值为<u>{{ getChildVal }}</u><br />
        <button @click="getChildValHandle">点击获取子组件的数据</button>
        <hr>
        <child />
        
    </div>
</template>
<script>
import Child from './child.vue'
export default {
    components: {
        Child
    },
    data() {
    	return {
    		title: '我是父组件中的数据',
    		getChildVal: ''
    	}
    },
    methods: {
    	getChildValHandle() {
    		this.getChildVal = this.$children[0].childTitle
    	}
    }
}
</script>
<template>
    <div>
        <p><span style="font-weight: bold; color: red; font-size: 24px;">我是子组件:</span>获取到父组件中的值为<u>{{ getParentVal }}</u></p>
        <button @click="getParentValHandle">点击获取父组件的数据</button>
    </div>
</template>
<script>
export default {
   data() {
   	return {
   		childTitle: '我是子组件中的数据',
   		getParentVal: ''
   	}
   },
   methods: {
   	getParentValHandle() {
   		this.getParentVal = this.$parent.title
   	}
   },
}
</script>
  1. 得到$parent$children的值不一样
  • $children的值是数组
  • $parent是个对象
  1. 边界情况
  • #app上拿$parent得到的是new Vue()的实例,在这实例上再拿$parent得到的是undefined
export default {
    created() {
        console.log(this.$parent.$parent) // undefined
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辛-夷

你的鼓励就是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值