最近寫了一個下拉組件,希望任意形式的觸發源點擊都可以展開這個組件。
最終效果圖如下:
方案一:slot-scope 傳方法
<!-- 僞代碼:下拉框組件 -->
<template>
<slot change-display="changeDisplay"></slot>
<div v-show="mVisiable">*下拉框代碼省略*<div>
<template>
<script>
export default {
data(){
return {
mVisiable: false
}
}
methods:{
changeDisplay(){
this.mVisiable = !this.mVisiable
}
}
}
</script>
<!--使用下拉彈窗組件-->
<dropdown v-model="value" :list="list">
<button slot-scope="{changeDisplay}"
@click="changeDisplay">{{value}}</button>
</dropdown>
不過這就導致每次用插槽都要寫 slot-scope 去取修改顯示狀態的函數。
方案二:找 VNode 中對應的頁面元素
插曲:
<!-- 僞代碼:下拉框組件 -->
<template>
<slot></slot>
<div v-show="mVisiable">*下拉框代碼省略*<div>
<template>
<script>
export default {
data(){
return {
mVisiable: false
}
}
methods:{
changeDisplay(){
this.mVisiable = !this.mVisiable
}
}
mounted() {
console.log(this.$slots)
}
}
</script>
<!--使用下拉彈窗組件-->
<dropdown v-model="value" :list="list">
<template v-slot>
<button>{{value}}</button>
</template>
</dropdown>
根據vue文檔 slot 被廢棄了(但還是兼容的)使用 v-slot 指定插槽,但是輸出VNode 中的 elm 是 undefined。
然後去查找 github vue issues 找到如下回答, 傳送門: issue
最後換回舊的插槽方法
<!--使用下拉彈窗組件-->
<dropdown v-model="value" :list="list">
<button>{{value}}</button>
</dropdown>
此時得到了插槽內的元素
修改下拉組件。爲VNode中的elm增加點擊事件。
<!-- 僞代碼:下拉框組件 -->
<template>
<slot></slot>
<div v-show="mVisiable">*下拉框代碼省略*<div>
<template>
<script>
export default {
data(){
return {
mVisiable: false
reference: undefined
}
}
methods:{
changeDisplay(){
this.mVisiable = !this.mVisiable
}
}
mounted() {
if (this.$slots.default) {
this.reference = this.$slots.default[0].elm
}
if (this.reference) {
this.reference.addEventListener('click', this.changeVisiable, false)
}
}
beforeDestroy() {
if (this.reference) {
this.reference.removeEventListener('click', this.changeVisiable, false)
}
}
}
</script>