vue最全的组件通信和插槽,看这一篇就够了
2021-10-09 本文已影响0人
江川哇
组件通信常用方式
props
父给子传值
//child
props: {msg: String}
//parent
<HelloWord msg="Hello"/>
自定义事件
子给父传值
$emit
//child
this.$emit('add', good)
//parent
<Cart @add="cartAdd($event)"></cart>
$bus事件总线
任意两个组件之间传值常用事件总线 或 vuex的方式。
1 .Vue.prototype.$bus = new Vue()
2.自己手写Bus 事件派发 监听和回调管理 有点类似手写promise里面的 resolve reject
class Bus {
constructor() {
this.callbacks = {}
}
$emit(name,args) {
if(this.callbacks[name]) {
this.callbacks[name].forEach(cb => cb(args))
}
}
$on(name,fn) {
this.callbacks[name] = this.callbacks[name] || []
this.callbacks[name].push(fn)
}
}
//main.js
Vue.prototype.$bus = new Bus()
//child1
this.$bus.$on('foo',handle)
//child2
this.$bus.$emit('foo')
event bus
vuex
唯一的全局数据管理者store,通过它管理数据并通知组件状态变更。
边界情况
$parent/$root
兄弟组件之间通信可以通过共同祖辈搭桥 $parent 或$root
//brother1
this.$parent.$on('foo', handle)
//brother2
this.$parent.$emit('foo')
$children
父组件可以通过$children访问子组件实现父子通信
//parent
this.$children[0].xxx = 'xxx'
//注意:$children 不能保证子元素顺序
$parent $children $root不建议使用 因为他们耦合性太强了。
$refs
获取子节点引用
//parent
<Helloworld ref='hw'></Hellowrold>
mounted() {
this.$refs.hw.xx = 'xxx'
}
provide/inject
能够实现祖先和后代之间的传值
provide() {
return {foo: 'foo'}
}
inject:['foo']
$attrs $listeners
非prop特性 ,可以包含所有父作用域的绑定(class style除外)并且可以通过v-bind=“$attrs”传入组件内部,创建高级组件时特别有用
//child :并未在props中声明foo
<p>{{$attrs.foo}}</p>
//parent
<HelloWorld foo="foo"></Helloworld>
插槽
插槽语法是vue实现的内容分发api 适合复合组件开发,在通用组件库的开发中大量使用。
匿名插槽
//comp1
<div>
<slot></slot>
</div>
//parent
<comp>Hello</comp>
具名插槽
将内容分发到子组件指定位置
//comp2
<div>
<slot></slot>
<slot name="content"></slot>
</div>
//parent
<comp2>
//默认插槽将default 作为参数
<template v-slot:default></template>
//具名插槽将插槽名作为参数
<template v-slot:content></template>
</copm2>
作用域插槽
分发内容到子组件的数据
//comp3
<div>
<slot :foo='foo'></slot>
</div>
//parent
<comp3>
//用v-slot的值指定作用域上下文对象
<template v-slot:default="slotProps">
来自子组件数据{{slotProps.foo}}
</template>
</comp3>
实现弹窗组件
弹窗这类组件的特点是他们在当前vue实例之外独立存在(Telport),通常挂载在body,,他们是通过js动态创建的,不需要在任何组件中声明。常见使用姿势
this.$create(Nitice, {
title: '标题',
message: '弹窗主体内容',
duration: '1000'
}).show()
create函数
//传入一个组件的配置
//创建她的实例,并且挂载到body上
import Vue from 'vue'
export default function Create (Component,props) {
//方式1、Vue.extend()
const Ctor = Vue.extend( )
//创建组件实例
const comp = new Ctor({propsData: props})
comp.$mount()
ducument.body.appenChild(comp.$el)
comp.remove = function () {
document.body.removeChild(comp.$el)
comp.$destroy()
}
//方式2 借鸡生蛋 借助vue的实例的render函数
const vm = new Vue({
render(h) {
return h (Component, { props })
}
}).$mount()
document.body.appendChild(vm.$el)
//删除函数
const comp = vm.$children[0]
comp.remove = () => {
vm.$destroy()
document.body.removeChild(vm.$el)
}
return comp
}
通知组件
建通知组件,Notice.vue
<template>
<div class="box" v-if="isShow">
<h3>{{title}}</h3>
<p class="box-content">{{message}}</p>
</div>
</template>
<script>
export default {
props: {
title: {
type: String,
default: ""
},
message: {
type: String,
default: ""
},
duration: {
type: Number,
default: 1000
}
},
data() {
return {
isShow: false
};
},
methods: {
show() {
this.isShow = true;
setTimeout(this.hide, this.duration);
},
hide() {
this.isShow = false;
this.remove();
}
}
};
</script>
<style>
.box {
position: fixed;
width: 100%;
top: 16px;
left: 0;
text-align: center;
pointer-events: none;
background-color: #fff;
border: grey 3px solid;
box-sizing: border-box;
}
.box-content {
width: 200px;
margin: 10px auto;
font-size: 14px;
padding: 8px 16px;
background: #fff;
border-radius: 3px;
margin-bottom: 8px;
}
</style>
使用create API
<script>
import create from '@/utils/create'
import Notice from '@/components.Notice'
export default {
methods: {
submitForm(form) {
const notice = create(Notice, {
title: '弹窗标题',
message:'弹窗内容',
duration: 1000,
})
notice.show()
}
}
}
</script>