组件、事件总线、动态组件、插槽
一、组件通信之父传子通信
1、父子通信
# 在全局组件上自定义属性
<navbar myname="cc" age="18"></navbar>
# 在组件中获取, 关键字:props
props: ['myname', 'age']
# 区分这两种赋值方式
<navbar myname="cc" age="18"></navbar>
<navbar :myname="'name'" age="18"></navbar>
<navbar :myname="name" age="18"></navbar>
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="js/vue.js"></script>
<title>Title</title>
</head>
<body>
<div id="box">
<navbar myname="jason" age="18"></navbar>
<navbar :myname="name" :age="age"></navbar>
<navbar :myname="'name'" :age="'age'"></navbar>
<!--'name'和'age'被直接当做字符串显示-->
</div>
</body>
<script>
// 全局组件
Vue.component('navbar', {
template: `
<div>
<button style="background: red">主页</button>
父组件传递的内容是:{{myname}}---{{age}}
</div>
`,
props: ['myname', 'age']
})
var vm = new Vue({
el: '#box',
data: {
name: 'cc',
age: 19,
}
})
</script>
</html>

2、属性验证
# 1、限制父传子的变量类型
示例:
props: {
myname:String,
isShow: Boolean
}
# 2、父传子时注意一下的区别
<navbar myname="cc" :isShow="isShow"></navbar>
<navbar myname="cc" :isShow="false"></navbar>
代码演示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="js/vue.js"></script>
<title>Title</title>
</head>
<body>
<div id="box">
<navbar myname="jason" :isShow="isShow"></navbar>
<navbar myname="jason" :isShow="false"></navbar>
</div>
</body>
<script>
// 全局组件
Vue.component('navbar', {
template: `
<div>
<button style="background: red">主页</button>
父组件传递的内容是:{{myname}}
</div>
`,
props: {
myname: String,
isShow: Boolean
}
})
var vm = new Vue({
el: '#box',
data: {
name: 'cc',
isShow: true
}
})
</script>
</html>

二、组件通信之子与父通信
1、子与父通信
# 通过事件实现:
点击一下子组件,就会触发父组件某个函数的执行
代码演示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="js/vue.js"></script>
<title>Title</title>
</head>
<body>
<div id="box">
<navbar @myevent="handleClick2"></navbar>
</div>
</body>
<script>
// 全局组件
Vue.component('navbar', {
template: `
<div>
<button @click="handleClick1">点击触发父组件的函数执行</button>
</div>
`,
methods: {
handleClick1() {
console.log('navbar组件的函数handleClick1执行')
this.$emit('myevent', 100, this.name, 99)
}
},
data() {
return {name: 'cc'}
}
})
var vm = new Vue({
el: '#box',
data: {
name: 'cc',
age: 19,
},
methods: {
handleClick2(ev, a, b) {
console.log('父组件的函数handleClick2执行')
console.log(ev)
console.log(a)
console.log(b)
}
}
})
</script>
</html>

2、小案例
子组件有一个按钮,有一个输入框,当输入完内容,点击按钮,数据在父组件中展示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="js/vue.js"></script>
<title>子与父通信小案例</title>
</head>
<body>
<div id="box">
<child @myevent="handle"></child>
<hr>
{{name}}
</div>
</body>
<script>
// 全局组件
Vue.component('child', {
template: `
<div>
<input type="text" v-model="myText">
<button @click="handleClick">点我</button>
</div>
`,
methods: {
handleClick() {
this.$emit('myevent', this.myText)
}
},
data() {
return {myText: ''}
}
})
var vm = new Vue({
el: '#box',
data: {
name: '',
},
methods: {
handle(text) {
this.name = text
}
}
})
</script>
</html>

执行流程图:

三、ref属性
# ref属性也可以实现组件间的通信,子对父,父对子 都可以
- ref放在标签上,拿到的是原生节点
- ref放在组件上,拿到的是组件对象,
- 子传父的方式:(this.$refs.mychild.text)
- 父传子的方式:(调用子组件的方式传参数)
1、使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="js/vue.js"></script>
<title>Title</title>
</head>
<body>
<div id="box">
<input type="text" ref="myref">
<hr>
<child ref="mychild"></child>
<hr>
<button @click="handleButton">点我</button>
</div>
</body>
<script>
Vue.component('child', {
template: `
<div>
<input type="text" v-model="mytext">
<hr>
我是子组件的input
</div>
`,
data() {
return {
mytext: ''
}
},
methods:{
add(a){
console.log('我是子组件的add方法')
console.log(a)
return '返回了'
}
}
})
var vm = new Vue({
el: '#box',
data: {
name: 'cc'
},
methods: {
handleButton(){
console.log('----',this.$refs)
console.log('----',this.$refs.myref.value)
console.log('----',this.$refs.mychild.mytext)
// 调用child的方法add
console.log('----',this.$refs.mychild.add(this.name))
}
}
})
</script>
</html>

四、事件总线
作用:不同层级的不同组件之间通信
代码演示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="js/vue.js"></script>
<title>Title</title>
</head>
<body>
<div id="box">
<child1></child1>
<hr>
<child2></child2>
</div>
</body>
<script>
// 定义一个事件总线
var bus = new Vue()
// 组件1
Vue.component('child1', {
template: `
<div>
<input type="text" v-model="mytext">
<button @click="handleClick">点击传数据到另一个组件</button>
</div>
`,
methods: {
handleClick() {
console.log('组件1要发送的消息:',this.mytext)
bus.$emit('send', this.mytext) // 通过事件总线发送
}
},
data() {
return {
mytext: '',
}
},
})
// 组件2
Vue.component('child2', {
template: `
<div>
收到的消息是:{{recv_text}}
</div>
`,
data() {
return {
recv_text: '',
}
},
mounted() { // 组件挂载(生命周期钩子函数中的一个,开始监听事件总线上的send)
bus.$on('send', (item) => {
console.log('组件2收到了:', item)
this.recv_text = item
})
},
})
var vm = new Vue({
el: '#box',
data: {}
})
</script>
</html>

五、动态组件
同一页面,点击切换组件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="js/vue.js"></script>
<title>动态组件</title>
</head>
<body>
<div id="box">
<ul>
<li @click="who='child1'">首页</li>
<li @click="who='child2'">商品</li>
<li @click="who='child3'">订单</li>
</ul>
<keep-alive>
<component :is="who"></component>
</keep-alive>
</div>
</body>
<script>
var vm = new Vue({
el: '#box',
data: {
who: 'child1'
},
components: {
child1: {
template: `
<div>
我是首页组件
<input type="text">
</div>
`
},
child2:{
template:`
<div>我是商品组件</div>
`
},
child3:{
template:`
<div>我是订单组件</div>
`
}
}
})
</script>
</html>
六、slot插槽
1、基本使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>slot插槽</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="box">
<child1>
<ul>
<li v-for="i in 4">{{i}}</li>
</ul>
</child1>
<hr>
<child2></child2>
<hr>
<child3></child3>
</div>
</body>
<script>
var vm = new Vue({
el: '#box',
data: {
who: 'child1'
},
components: {
child1: {
template: `
<div>
<slot></slot>
<hr>
我是首页组件
</div>
`
},
child2: {
template: `
<div>我是商品组件</div>
`
},
child3: {
template: `
<div>我是订单组件</div>
`
}
}
})
</script>
</html>

2、小案例:一个组件通过插槽控制另一个组件的显示/隐藏
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>slot插槽控制另一个组件的显示</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="box">
<child1>
<button @click="isShow=!isShow">点击显示/隐藏child2</button>
</child1>
<child2 v-show="isShow"></child2>
</div>
</body>
<script>
var vm = new Vue({
el: '#box',
data: {
isShow: true
},
components: {
child1: {
template: `
<div>
<slot></slot>
<hr>
</div>
`
},
child2: {
template: `
<ul>
<li v-for="i in 4">{{i}}</li>
</ul>
`
}
}
})
</script>
</html>

3、具名插槽
通过slot名称,指定标签放到组件的某个插槽中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>指定标签放到组件的某个插槽中</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="box">
<child1>
<div slot="div1">我是div</div>
<button @click="isShow=!isShow" slot="button1">点击显示/隐藏child2</button>
</child1>
<hr>
<child2 v-show="isShow"></child2>
</div>
</body>
<script>
var vm = new Vue({
el: '#box',
data: {
isShow: true
},
components: {
child1: {
template: `
<div>
<slot name="button1"></slot>
<hr>
<h5>手动分割</h5>
<hr>
<slot name="div1"></slot>
</div>
`
},
child2: {
template: `
<ul>
<li v-for="i in 4">{{i}}</li>
</ul>
`
}
}
})
</script>
</html>
button1和div1的位置是组件中写的slot位置决定的:
