组件间通信方式

方式一:props

适用于:父子组件间通信

1、父给子

父组件给子组件传递数据(非函数):本质其实是父组件----->子组件传递数据

//父组件App.vue
<template>
	<div>
		<Student name="李四" sex="女" :age="18"/>
	</div>
</template>
<script>
	import Student from './components/Student'
	export default {
		components:{Student}
	}
</script>
//子组件
<template>
	<div>
		<h2>学生姓名:{{name}}</h2>
		<h2>学生性别:{{sex}}</h2>
		<h2>学生年龄:{{myAge+1}}</h2>
		<button @click="updateAge">尝试修改收到的年龄</button>
	</div>
</template>
<script>
	export default {
		data() {
			return {
				myAge:this.age
			}
		},
		methods: {
			updateAge(){
				this.myAge++
			}
		},
		//props的三种接收方式
		//1、简单声明接收
		// props:['name','age','sex'] 

		//2、接收的同时对数据进行类型限制
		/* props:{
			name:String,
			age:Number,
			sex:String
		} */

		//3、接收的同时对数据:进行类型限制+默认值的指定+必要性的限制
		props:{
			name:{
				type:String, //name的类型是字符串
				required:true, //name是必要的
			},
			age:{
				type:Number,
				default:99 //默认值
			},
			sex:{
				type:String,
				required:true
			}
		}
	}

2、子给父

父组件给子组件传递数据(函数类型):本质其实是子组件----->父组件传递数据
通过父组件给子组件传递函数类型的props实现:子给父传递数据

//父组件APP.vue中
<template>
	//1、父给子传递一个函数
	<School :getSchoolName="getSchoolName"/>
</template>
<script>
export default {
	components:{School},
	data() {
			return {
				schoolName:''
			}
		},
	methods:{
		//2、声明这个函数
		getSchoolName(name){
				console.log('App收到了学校名:',name)
				this.schoolName = name
			},
	}
}
</script>
//子组件School.vue中
<template>
	//4、写点击事件
	<button @click="sendSchoolName">把学校名给App</button>	
</template>
<script>
.......
export default {
	//3、子组件声明接收函数
	props:['getSchoolName'],
	data(){
		return {
			name:'xxxxxx'
		}
	},	
	methods: {
			//5、写点击事件的函数
			sendSchoolName(){
				//6、调用接收到的函数,传参数
				this.getSchoolName(this.name)
			}
		},
}
</script>

方式二、组件自定义事件

适用于:子给父传递数据。通过父组件给子组件绑定一个自定义事件

1、子给父

写法1($emit)

在谁身上绑定的自定义事件就去谁身上触发(或解绑),下面代码中给子组件Student身上绑定(用@/v-on)了自定义事件demo,应该去Student身上触发(用$emit)

//父组件App.vue
<template>
	//1、通过父组件给子组件绑定一个自定义事件demo实现:子给父传递数据(使用@或v-on)。这个事件demo对应一个回调函数getStudentName
	<Student @demo="getStudentName" />
</template>
<script>
.........
	export default {
		components:{Student},
		data() {
			return {
				studentName:''
			}
		},
		methods: {
			//2、声明回到调函数  当子组件用$emit触发这个自定义事件demo后会执行事件对应的回调函数,这个回调函数能接收到触发自定义事件的组件(即本例的子组件)传过来的参数
			getStudentName(name,...params){
				console.log('App收到了学生名:',name,params)
				this.studentName = name
			},
</script>
//子组件
<template>
	<div class="student">
		//3、给点击事件绑定回调函数
		<button @click="sendStudentlName">把学生名给App</button>
		<button @click="unbind">解绑demo事件</button>
		<button @click="death">销毁当前Student组件的实例(vc)</button>
	</div>
	<script>
	export default {
		name:'Student',
		data() {
			return {
				name:'张三',
			}
		},
		methods: {
			//4、写触发点击事件后调用的回调函数
			sendStudentlName(){
				//5、用$emit方法触发Student组件实例身上的demo事件,同时把子组件的数据传给了父组件
				this.$emit('demo',this.name,666,888,900)
			},
			unbind(){
				this.$off('demo') //解绑一个自定义事件
				// this.$off(['demo','demo1']) //解绑多个自定义事件
				// this.$off() //解绑所有的自定义事件
			},
	</script>
</template>

写法2($on)

与写法1不同的是,用ref代替了v-on/@,同时增添了一个监听($on)的步骤

//父组件App.vue
<template>
	//1、通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(使用@或v-on)
	<Student ref="student"/>
</template>
<script>
.........
	export default {
		components:{Student},
		data() {
			return {
				studentName:''
			}
		},
		methods: {
			//3、声明demo事件对应的回调函数
			getStudentName(name,...params){
				console.log('App收到了学生名:',name,params)
				this.studentName = name
			},
		mounted() {
			//2、
			//this.$refs.student:拿到了组件的实例对象Student。然后用$on方法监听demo自定义事件,如果demo事件被触发,则调用demo事件对应的回调函数,同时这个回调能收到传过来的参数
			this.$refs.student.$on('demo',this.getStudentName) 
			//绑定自定义事件(一次性)
			// this.$refs.student.$once('atguigu',this.getStudentName) 
		},
	}
</script>
//子组件与写法1中的相同

方式三、全局事件总线$bus

适用于:任意组件间通信(子给父、父给子、兄弟给兄弟)

任意组件间

用兄弟组件间通信来举例

//main.js
.............
new Vue({
	el:'#app',
	render: h => h(App),
	beforeCreate() {
		Vue.prototype.$bus = this //1、安装全局事件总线。让所有组件都能用$bus
	},
})

student组件给school组件传数据
school组件作为想要接收数据的一方,需要先在全局事件总线上绑定自定义事件,由于在自己组件内给$bus绑定的事件,该事件对应的回调函数自然就留在了自己组件内。

//school组件
<template>
	......
</template>
<script>
export default {
  name: "School",
  mounted() {
    // console.log('School',this)
    // 在这个组件内给全局事件总线$bus绑定了自定义事件hello,
    // 但是这个自定义事件对应的回调是留在了这个组件内
    // 如果别的组件触发$bus身上的自定义事件hello,该事件对应的回调就会执行
    // 该回调在那个组件内,那个组件就能收到触发事件的组件传过来的数据
    //2、
    this.$bus.$on("hello", (data) => {
    	//6、执行自定义事件对应的回调,同时能接收到数据
      console.log("我是School组件,收到了数据", data);
    });
  },
  beforeDestroy() {
  	//解绑自定义事件
    this.$bus.$off("hello");
  },
};
</script>

如果student组件想给school组件传递数据,就可以触发school组件在$bus身上绑定的自定义事件,同时传递数据。由于自定义事件被触发,对应的回调函数(留在school组件内)也会执行,该回调函数能接收到传递过来的数据

//student组件
<template>
	<div class="student">
		<h2>学生姓名:{{name}}</h2>
		<h2>学生性别:{{sex}}</h2>
		//3、绑定点击事件
		<button @click="sendStudentName">把学生名给School组件</button>
	</div>
</template>

<script>
	export default {
		name:'Student',
		data() {
			return {
				name:'张三',
				sex:'男',
			}
		},
		methods: {
		   //4、声明点击事件对应的回调
			sendStudentName(){
				//5、该回调触发自定义事件,同时传递数据
				this.$bus.$emit('hello',this.name)
			}
		},
	}
</script>

方式四、消息订阅与发布pubsub.js

适用于:任意组件间通信(react中用的多,vue中用的少)

任意组件间

用兄弟组件间通信来举例
student组件给school组件传数据

//安装库
npm i pubsub-js

school组件拿到student组件的数据,即school组件是作为订阅消息的一方。

//school组件
<template>
	..............
</template>
<script>
	//引入库
	import pubsub from 'pubsub-js'
	export default {
		name:'School',
		mounted() {
			//订阅消息hello,如果发布了消息hello,就执行该消息对应的回调函数,同时拿到传递的数据。msgName是消息名,data才是传递的数据。
			//每次订阅都是不同的id
			this.pubId = pubsub.subscribe('hello',(msgName,data)=>{
				console.log(this)
				// console.log('有人发布了hello消息,hello消息的回调执行了',msgName,data)
			})
		},
		beforeDestroy() {
			pubsub.unsubscribe(this.pubId)
		},
	}
</script>

而student组件是发布消息的一方

//student组件
<template>
	<div class="student">
		<h2>学生姓名:{{name}}</h2>
		<h2>学生性别:{{sex}}</h2>
		//点击事件
		<button @click="sendStudentName">把学生名给School组件</button>
	</div>
</template>
<script>
	import pubsub from 'pubsub-js'
	export default {
		name:'Student',
		data() {
			return {
				name:'张三',
				sex:'男',
			}
		},
		methods: {
			//点击事件对应的回调函数
			sendStudentName(){
				//执行该回调函数,则发布消息hello,同时传递数据
				pubsub.publish('hello',666)
			}
		},
	}
</script>

方式五、Vuex

适用于:任意组件间通信

方式六、插槽

适用于:父子组件通信(一般结构)

默认插槽

具名插槽

作用域插槽

你可能感兴趣的:(Vue,vue.js,javascript,前端)