b站视频链接
今天面试被问到了兄弟组件通信,突然有点懵,只想出了一个全局事件总线
Vue 兄弟组件之间的通信
适用场景:父子组件通信
适用场景:子组件给父组件通信
$on
与$emit
适用场景:万能
Vue.prototype.$bus=this;
$emit
发布事件,$on
监听事件
详情参考这篇
适用场景:万能
React框架中使用比较多,套路:发布与订阅
适用场景:万能
适用场景:父子组件通信
三种:默认插槽、具名插槽、作用域插槽
event
:事件对象
button
可以绑定click
单击事件等Event
组件绑定click
事件,在此click
事件就不是原生事件,而属于自定义事件。若想给组件标签绑定原生事件,加个时间修饰符.native
即可。也就是
当前原生DOM click
事件,其实是给组件的根标签绑定了点击事件,此处利用了事件委派.natve
在vue3中已被废弃$emit
函数v-model
是Vue框架中的指令,主要结合表单元素(比如文本框、复选、单选按钮等)一起使用,主要作用是收集表单元素,它也是组件通信方式的一种,可以实现数据的双向绑定。
<template>
<div>
<h2>深入v-model</h2>
<input type="text" v-model="msg">
<span>{{msg}}</span>
<br>
</div>
</template>
<script type="text/ecmascript-6">
// import CustomInput from './CustomInput.vue'
export default {
name: 'ModelTest',
components: {
// CustomInput
},
data(){
return {
msg:'赵丽颖'
}
}
}
</script>
<input type="text" :value="msg" @input="msg = $event.target.value">
Vue2可通过value与input事件实现v-mdel功能:原生DOM中有oninput
事件,它经常结合表单元素一起使用,当表单元素文本内容发生变化时就会触发一次回调。
<input type="text" :value="msg" @input="msg = $event.target.value">
此处的:value
是给原生DOMinput
绑定一个响应式属性
<CustomInput :message="msg" @input="msg = $event"></CustomInput>
此处的:value
是props
,父子组件通信
子组件如下:
<template>
<div style="background: #ccc; height: 50px;">
<h2>input包装组件</h2>
<input type="text" :value="message" @input="$emit('input',$event.target.value)">
</div>
</template>
<script type="text/ecmascript-6">
export default {
name: 'CustomInput',
props:['message']
}
</script>
<!-- :money 父组件给子组件传递props
@update:money 给子组件绑定的自定义事件 名字是update:money
这种操作其实和v-model很相似,可以实现父子组件数据同步
-->
<Child :money="money" @update:money="money = $event"></Child>
<!-- :money.sync 含义:
第一,父组件给子组件传递props --money
第二,给当前子组件绑定了一个自定义事件 而且事件名称即为update:money
-->
<Child :money.sync="money"></Child>
注:.sync
和v-model
用在组件标签上,都可以达到父子组件数据同步
区别:
.sync
达到数据同步: 子组件内部不是表单类元素
v-model
达到数据同步:子组件内部一定是表单类元素
组件实例自身的$attrs
与$listeners
属性:
$attrs
属于组件的一个属性,可以获取到父组件传递过来的props
数据。 对于子组件而言,父组件给的数据可以利用props
接收,但要注意,子组件通过props
接收的属性在$attrs
属性当中是获取不到的。
$listeners
可以获取到父组件给子组件传递的自定义事件
对el-button
进行二次封装:自定义带Hover提示的按钮(利用a
标签的title
属性)
子组件如下:
:type="$attrs.type"
这种写法是一个一个绑定,用v-bind="$attrs"
可以把$attrs
接收到的所有属性绑定到元素上
<template>
<a href="javascript:;" :title="title" style="margin-right:10px">
<!-- 此处v-bind不能用 : 代替 v-on不能用@代替 -->
<el-button v-bind="$attrs" v-on="$listeners"></el-button>
</a>
</template>
<script>
export default {
name: '',
props:['title'],
mounted(){
//this.$attrs 就是父组件传递过来的所有属性(除了props接收的以及style和class)组成的对象
//this.$listeners 就是父组件给子组件绑定的所有事件监听组成的对象
//通常使用他们就是为了对一个组件进行二次封装
console.log(this.$attrs,this.$listeners)
}
}
</script>
用ref
实现BABA找儿子借钱,ref
可以获取真实DOM节点,当然也可以获取子组件标签(操作子组件的数据与方法):
<button @click="borrowMoneyFromXM(100)">找小明借钱100</button><br>
<Son ref="son" />
borrowMoneyFromXM(sonmoney){
//父亲的钱要加 + 100
this.money += sonmoney
//儿子小明的钱要 - 100
this.$refs.son.money -= sonmoney
},
用同样的方法可以实现向女儿借钱
当同时向儿子和女儿借钱时:
<button @click="borrowMoneyFromALL(200)">找所有孩子借钱200</button><br>
borrowMoneyFromALL(money){
this.money += money * 2
//儿子和女儿都要减去200
// 组件实例自身拥有一个属性$children 可以获取当前组件当中的所有子组件
console.log(this.$children)
this.$children.forEach(item => item.money -= money)
// this.$children[0] 不一定是谁,因此官方建议尽量不使用$children和$parent
}
当儿子女儿给BABA钱时,也就是子组件操作父组件的数据时:
需要在子组件内部,获取到父组件,让父组件的数据发生变化。可以通过$parent属性获取到某一个组件的父组件,然后操作父组件的数据和方法
<button @click="giveMoney(50)">给BABA钱: 50</button>
giveMoney(money){
this.money -= money
// this.$parent可以拿到父组件对象,操作父组件对象的数据
this.$parent.money += money
}
ref
可以获取到当前组件的子组件
$children
可以获取到当前组件的全部子组件(以数组形式,但其元素顺序不确定)
项目中出现很多结构类似的地方:想到组件复用
项目中很多组件的JS业务逻辑相似:想到mixin
(可以把多个组件JS重复的部分封装成一个mixin
)
比如把儿子女儿给BABA钱这个操作封装在myMixin.js中
(除了methods
,还可以封装mounted
、computed
等)
//定义混入
export default {
methods: {
giveMoney(money){
this.money -= money
// this.$parent可以拿到父组件对象,操作父组件对象的数据
this.$parent.money += money
},
},
mounted(){
console.log(111234)
}
}
子组件
<template>
<div style="background: #ccc; height: 50px;">
<h3>儿子小明: 有存款: {{money}}</h3>
<button @click="giveMoney(50)">给BABA钱: 50</button>
</div>
</template>
<script>
import myMixin from '@/pages/Communication/ChildrenParentTest/myMixin'
export default {
name: 'Son',
mixins:[myMixin],
data () {
return {
money: 30000
}
},
</script>
可以实现父子组件通信
默认插槽
具名插槽
作用域插槽:子组件的数据来源于父组件,且子组件决定不了自身的结构与样式
作用域插槽完成的事情:
父子之间通信
1\ 数据是在父组件当中的
2\ 数据最终传递给了子组件进行展示v-for
3\ 子组件在展示数据的过程中,数据的结构是由父组件说了算的
父组件
<h2>效果一: 显示TODO列表时, 已完成的TODO为绿色</h2>
<List :todos="todos">
<template slot-scope="scopeProps">
<!-- scopeProps最终是一个对象,接收子组件作用域插槽回传给父组件的数据组成的对象-->
<span :style="{color:scopeProps.todo.isComplete?'green':'black'}">{{scopeProps.todo.text}}</span>
</template>
</List>
<hr>
<h2>效果二: 显示TODO列表时, 带序号, TODO的颜色为蓝绿搭配</h2>
<List :todos="todos">
<template slot-scope="{todo,index}">
<h2 :style="{color:index % 2===0?'blue':'green'}"><span>{{index+1}}</span>{{todo.text}}</h2>
</template>
</List>
子组件
<template>
<ul>
<li v-for="(todo, index) in todos" :key="index">
<!-- :todo="todo" 不是props组件通信,作用域插槽通信 :把数据回传给父组件-->
<slot :todo="todo" :index="index">{{todo.text}}</slot>
</li>
</ul>
</template>
<script>
export default {
name: 'List',
props: {
todos: Array
}
}
</script>
插槽在使用第三方组件库时会经常用到