非父子组件之间的通信
provide和inject的基本使用。
在上层组件中使用provide来定义数据给其子孙使用:
import Home from "./Home.vue";
export default {
components:{
Home
},
provide:{
name:"sk",
age:18
}
}
</script>在子孙组件中使用inject来使用上层组件定义的数据:
<template>
<div>
HomeContent: {{name}} - {{age}}
</div>
</template>
<script>
export default {
inject:["name","age"]
}
</script>Provide引用data和响应式
provide传递data中的数据,应该将provide定义为function,然后return一个对象(就如同data的使用方式一样),这样就能使用this来访问data中的数据:
provide(){
return {
name:"sk",
age:18,
length:this.names.length
}
},
data(){
return{
names:["abc","cba","nba"]
}
},如果想让provide的数据是响应式的,就需要使用vue中的computed方法,该方法返回的是一个ref对象,需要使用value来取出值:
// App.vue
provide(){
return {
name:"sk",
age:18,
// ref对象,value
length: computed(()=>this.names.length)
}
},
// HomeContent.vue
<template>
<div>
HomeContent: {{name}} - {{age}} - {{length.value}}
</div>
</template>事件总线库mitt的使用
首先安装mitt:
npm install mitt写一个工具类eventBus.js导出一个emitter实例:
import mitt from "mitt";
const emitter = mitt();
export default emitter;通过键值对的方式向emitter注册一个消息:
methods: {
btnClick(){
console.log("about按钮点击");
emitter.emit("why",{name:"sk",age:18});
emitter.emit("kobe",{name:"kobe",age:30});
}
},在一个组件的created生命周期中注册监听一个消息:
created() {
emitter.on("why",(info)=>{
console.log("why",info);
});
emitter.on("kobe",(info)=>{
console.log("kobe",info);
});
// *表示所有的事件
emitter.on("*",(type,info)=>{
console.log("* listener",type,info);
})
// 取消emitter的所有监听
// emitter.all.clear();
// 定义一个函数
// function onFoo(){}
// emitter.on("foo",onFoo);
// emitter.off("foo",onFoo);
},插槽
基本使用:
使用slot标签在子组件中定义一个插槽:
<template>
<div>
<h2>组件的开始</h2>
<slot></slot>
<h2>组建的结束</h2>
</div>
</template>在父组件中使用这个子组件,并在子组件标签中放入插槽元素:
<template>
<div>
<my-slot-cpn>
<button>我是按钮</button>
<my-button/>
</my-slot-cpn>
</div>
</template>在slot标签中添加元素可以指定插槽的默认元素:
<template>
<div>
<h2>组件的开始</h2>
<slot>
<i>我是默认的i元素</i>
</slot>
<h2>组建的结束</h2>
</div>
</template>具名插槽
通过给slot标签增加name属性来设置具名插槽:
<template>
<div class="nav-bar">
<div class="left">
<slot name="left"></slot>
</div>
<div class="center">
<slot name="center"></slot>
</div>
<div class="right">
<slot name="right"></slot>
</div>
</div>
</template>使用过程中通过template的v-slot属性来指定使用某一个插槽:
<template>
<div>
<nav-bar :name="name">
<template v-slot:left>
<button>左边的按钮</button>
</template>
<template v-slot:center>
<h2>我是标题</h2>
</template>
<template v-slot:right>
<button>右边的按钮</button>
</template>
</nav-bar>
</div>
</template>动态命名插槽:可以结合props来动态指定一个插槽的名称:
// NavBar.vue
<template>
<div class="nav-bar">
<div class="addition">
<slot :name="name"></slot>
</div>
</div>
</template>
<script>
export default {
props:{
name:String
}
}
</script>
// App.vue
<template>
<div>
<nav-bar :name="name">
<template v-slot:[name]>
<button>动态名称</button>
</template>
</nav-bar>
</div>
</template>
<script>
import NavBar from "./NavBar.vue";
export default {
components:{
NavBar
},
data() {
return {
name:"sk"
}
},
}
</script>插槽有默认的名称default。
v-slot:的缩写是#
作用域插槽
作用域的概念是:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的;
在slot标签中增加属性,此属性的值可以在父组件中通过template的v-slot获取到一个实例(如slotProps),并通过slotProps来访问slot的属性:
// ShowNames.vue
<template>
<div>
<template v-for="(item,index) in names">
<slot :item="item" :index="index"></slot>
</template>
</div>
</template>
<script>
export default {
props:{
names:{
type: Array,
default:()=>[]
}
}
}
</script>
// App.vue
<template>
<div>
<show-names :names="names">
<template v-slot="slotProps">
<button>{{slotProps.item}}-{{slotProps.index}}</button>
</template>
</show-names>
</div>
</template>如果是具名插槽,在父组件的template中可以指定某一个具体的名称:
<template v-slot:left="slotProps">独占默认插槽的缩写:如果只有一个插槽,并且是非具名的,可以有以下缩写
<show-names :names="names" v-slot="slotProps">
<button>{{slotProps.item}}-{{slotProps.index}}</button>
</show-names>