本文主要探讨Vue组件三方面的内容
- 组件的通信方式:不同关系的组件分别如何传递数据
- 插槽:组件如何把内容插入到另一个组件
- 组件的设计思想,通过三个实例,探讨vue如何自定义普通组件,动态组件和递归组件
一、vue组件的通信方式
1、父传子:props
<!--Child.vue-->
<template>
<div>
<p>来自父组件的静态数据:{{staticMes}} </p>
<p>来自父组件的动态数据:{{dynamicMes}} </p>
</div>
</template>
<script>
export default {
props: ['staticMes', 'dynamicMes']
}
</script>
<!--Parent.vue-->
<template>
<div>
<Child staticMes="传给子元素的静态数据" :dynamicMes = "parentDynamicMes"/>
</div>
</template>
<script>
import Child from "Child.vue"
export default {
components: {
Child
},
data() {
return {
parentDynamicMes: '传给子元素的动态数据'
}
},
}
</script>
2、子传父:事件
- 子组件触发[$emit]事件,把数据作为参数带上
- 父组件监听事件,并获取参数[数据]
<!--Child.vue-->
<template>
<div>
<p>子组件的数据:{{childMes}}</p>
<button @click="sendMes">把子组件的数据传给父组件</button>
</div>
</template>
<script>
export default {
data() {
return {
childMes: '我是子组件的数据'
}
},
methods: {
sendMes() {
this.$emit('sendMesFromChild', this.childMes)
}
},
}
</script>
<!--Parent.vue-->
<template>
<div>
<Child @sendMesFromChild="receiveMes"/>
</div>
</template>
<script>
import Child from "Child.vue"
export default {
components: {
Child
},
methods: {
receiveMes(mes) {
console.log('我通过监听事件接收到来自子组件的数据:', mes)
}
},
}
</script>
3、兄弟组件:通过共同的父组件搭桥
通过共同的父组件,用事件把数据传给父组件,再由父组件传给子组件
<!--ChildA.vue-->
<template>
<div>
<p>子组件A的数据:{{childAMes}}</p>
<button @click="sendMes">把子组件的数据传给父组件</button>
</div>
</template>
<script>
export default {
data() {
return {
childAMes: '我是子组件A的数据'
}
},
methods: {
sendMes() {
this.$parent.$emit('sendMesFromChildA', this.childAMes)
}
},
}
</script>
<!--ChildB.vue-->
<template>
<div>
<p>来自子ChildA的数据:{{childAMesShowInChildB}}</p>
</div>
</template>
<script>
export default {
props: ['childAMesShowInChildB']
}
</script>
<!--Parent.vue-->
<template>
<div>
<!--ChildA通过this.$parent触发了sendMesFromChildA事件,所以不需要在这里进行监听,直接在下面的mounted()钩子中进行监听-->
<ChildA/>
<ChildB :childAMesShowInChildB="childAMesRecInParent"/>
</div>
</template>
<script>
import ChildA from "ChildA.vue"
import ChildB from "ChildB.vue"
export default {
components: {
ChildA,
ChildB
},
data() {
return {
childAMesRecInParent: ''
}
},
mounted () {
this.$on('sendMesFromChildA', mes => {
// 监听sendMesFromChildA事件,捕获ChildA组件的数据【通过mes参数】,把mes赋值给this.childAMesRecInParent后,因为ChildB通过props的方式引用了this.childAMesRecInParent,所以ChildA的数据【childAMes】就通过父组件搭桥的方式传到了ChildB【childAMesShowInChildB】。
this.childAMesRecInParent = mes
})
},
}
</script>
4、祖先和后代:provide && inject
<!--Ancestor.vue-->
<template>
<div>
<p>我是祖先组件</p>
<Mid/>
</div>
</template>
import Mid from "Mid.vue"
<script>
export default {
components: {
Mid,
},
// 通过provide()抛出数据
provide() {
return {
mesFromAncestor: '一条来自祖先组件的信息'
};
},
}
</script>
<!--Mid.vue-->
<template>
<div>
<p>我是中间组件</p>
<Progeny/>
</div>
</template>
import Progeny from "Progeny.vue"
<script>
export default {
components: {
Progeny,
},
}
</script>
<!--Progeny.vue-->
<template>
<div>
<p>我是后代组件</p>
<p>接收来自祖先的信息:{{mesFromAncestor}}</p>
</div>
</template>
<script>
export default {
// 通过inject注入
inject: ['mesFromAncestor'],
}
</script>
5、任意组件:事件总线 || Vuex
5.1、事件总线:创建一个Bus类[Vue.prototype.$bus = new Vue()]负责事件派发、监听和回调管理
<!--CompA.vue-->
<template>
<div>
<p>我是在任意位置的一个A组件</p>
<button @click="sendMes">发送信息<button/>
</div>
</template>
<script>
export default {
data() {
return {
CompAMes: '我是来自CompA的信息'
}
},
methods: {
sendMes() {
// 通过this.$bus.$emi触发听事件,并抛出数据
this.$bus.$emit('busTest', this.CompAMes)
}
},
}
</script>
<!--CompB.vue-->
<template>
<div>
<p>我是在任意位置的一个B组件</p>
</div>
</template>
<script>
export default {
mounted() {
// 通过this.$bus.$on监听事件,并捕获数据
this.$bus.$on("busTest", mes => {
console.log(mes);
});
}
}
</script>
5.2、Vuex: 创建唯一的全局数据管理者store,通过它管理数据并通知组件状态变更【关于Vuex后续再做探讨】
二、vue组件插入内容的方式 —— 插槽
<!--Popup.vue-->
<template>
<div>
<!-- 具名插槽 -->
<slot name="header"></slot>
<!-- 匿名插槽 -->
<slot></slot>
<!-- 作用域插槽 -->
<slot name="footer" :footerProp="footerTitle"></slot>
</div>
</template>
<script>
export default {
data() {
return {
footerTitle: {
confirm: '确定',
cancel: '取消',
}
}
},
};
</script>
<!--UsePopup.vue-->
<template>
<div>
<h3>通过弹窗实例展示插槽用法</h3>
<Popup>
<!--具名插槽替换内容-->
<template #header>
<h5>温馨提示</h5>
</template>
<!--匿名插槽替换内容-->
<p>您要提交吗?</p>
<!--作用域插槽:正常来说,父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的,利用作用域插槽可以实现在父级模板中使用子模板的数据-->
<template v-slot:footer="slotProps">
<ul>
<li>{{slotProps.footerProp.cancel}}</li>
<li>{{slotProps.footerProp.confirm}}</li>
</ul>
</template>
</Popup>
</div>
</template>
<script>
import Popup from "Popup.vue"
export default {
components: {
Popup
},
};
</script>
三、vue组件设计思想 —— 自定义组件
- 3.1、组件通信和插槽综合运用实例:模仿Element-UI设计一个Vue的Form表单组件
- 3.2、创建组件实例:Vue弹窗类组件的设计与实现
- 3.3、以Tree组件为例: Vue递归组件的设计与实现