Element Plus 组件库实现:2. Collapse(2)

前言

上篇文章已经介绍了Collapse组件的大概实现方案及思路,Element Plus 组件库实现:2. Collapse(1) - 掘金 (juejin.cn),本文将简单介绍实现具体实现。

Collapse组件实现

html 复制代码
// 先引入需要用到的接口或方法
<script setup lang="ts">
import { ref, provide, watch } from 'vue';
import { collapseContextKey } from './types';
import type { NameType, CollapseProps, CollaspeEmits } from './types';

defineOptions({
    name: 'YvCollapse'
})
const props = defineProps<CollapseProps>()
const emits = defineEmits<CollaspeEmits>()
</script>

对应上篇文章的设计思路:

  • 在Collapse组件中用一个数组来保存打开的item的name属性
ts 复制代码
const activeNames = ref<NameType[]>(props.modelValue)
// 因为初始值为props.modelValue,来自父组件,所以这里还需要来设置一个监听以保证数据响应式
watch(() => props.modelValue, () => {
    activeNames.value = props.modelValue
})
  • 点击item的时候,先在数组中实现了打开和关闭的状态改变
ts 复制代码
const handleItemClick = (item: NameType) => {
    // 找到当前点击列表的索引值
    const index = activeNames.value.indexOf(item)
    // 先来到手风琴效果分支
    if (props.accordion) {
        // 如果点击的是展开项,那就清空激活数组,否则替换,这样就保证了只有一项被激活
        activeNames.value = [activeNames.value[0] === item ? '' : item]
    } else {
        if (index > -1) {
            // 存在, 删除相应的一项
            activeNames.value.splice(index, 1)
        } else {
            // 不存在, 插入相应的一项
            activeNames.value.push(item)
        }
        // console.log(activeNames.value)
    }
    // 派发事件,让父组件知道数据被改变
    emits('update:modelValue', activeNames.value)
    emits('change', activeNames.value)
}
  • 这个数组将由Collapse组件传递给CollapseItem

这里由于使用的slot,所以不能再使用prop来向子组件传递属性和方法,要用到provide和inject依赖注入的方法来传递:

ts 复制代码
// Symbol("collapseContextKey") 创建了一个唯一的符号,用于作为这个注入键
export const collapseContextKey: InjectionKey<CollapseContext> = Symbol("collapseContextKey");
ts 复制代码
// Collapse.vue
provide(collapseContextKey, {
    activeNames,
    // 将方法也传递到CollapseItem中,在点击Item列表时调用
    handleItemClick
})

CollapseItem组件实现

html 复制代码
// CollapseItem.vue
<script setup lang="ts">
import { inject, computed } from 'vue';
import { collapseContextKey } from './types';
import type { CollapseItemProps } from './types';

defineOptions({
    name: "YvCollapseItem"
})
const props = defineProps<CollapseItemProps>()
</script>


<template>
    <div class="yv-collapse-item" :class="{
        'is-disabled': props.disabled
    }">
        <div class="yv-collapse-item__header" :class="{
            'is-disabled': disabled,
            'is-active': isActive

        }" :id="`item-header-${props.name}`" @click="handleClick">
            <slot name="title">{{ title }}</slot>
        </div>
            <div class="yv-collapse-item__wrapper" v-show="isActive">
                // 这里绑定个id属性,使每个列表有唯一的id
                <div class="yv-collapse-item__content" :id="`item-content-${props.name}`">
                    <slot></slot>
                </div>
            </div>
    </div>
</template>

这个时候,子组件要做的事情显而易见了 => 根据父组件Collapse传递过来的状态数组activeNames,决定展示或隐藏相应的列表项,对应上篇文章的设计思路:

  • 然后在Item组件内部,也就是CollapseItem组件内部,判断name是否存在于数组中,使用一个计算属性结合v-show,真正实现打开和关闭
ts 复制代码
// 接受祖先组件传递过来的参数,默认值为undefined
const collapseContext = inject(collapseContextKey, undefined)
// 根据是否被添加到数据进行显示/隐藏处理
const isActive = computed(() => collapseContext?.activeNames.value.includes(props.name))
const handleClick = () => {
    // 禁用状态下当然就直接返回了
    if (props.disabled) { return }
    // 触发点击,并把当前列表独有的name属性传递给Collapse
    collapseContext?.handleItemClick(props.name)
}

这样,两个组件的就基本实现,关于样式和动画,不再赘述,也不是本文重点。

总结

本文通过结合上篇文章Element Plus 组件库实现:2. Collapse(1) - 掘金 (juejin.cn)的方案及思路完成了对Collapse组件和CollapseItem组件的具体实现,需要注意的是,这里的传递数据需要通过provide和inject依赖注入的方法来传递。

相关推荐
wangxiaoxuan2171 小时前
SLAM 的基本结构:前端、后端、回环与地图优化
前端·机器人·科沃斯八界
炫酷的大高个2 小时前
98% 连跑 20 次只剩 67%,我算了大模型重试账
人工智能·架构·开源
分布式存储与RustFS4 小时前
镜像仓库的层别只堆本地盘:用 RustFS 给 Harbor 当 S3 后端
运维·云原生·开源·对象存储·分布式存储·s3·性能基准
Agent世界4 小时前
用 CDP 做发布自动化:四道机器闸门与产物验收
前端·javascript
FungLeo4 小时前
成为全栈·Vue 管理后台篇·编辑器图片上传:从文件选择到文章附件关联
前端·vue.js·编辑器·markdown·文件上传·formdata·附件管理
某哈压力大5 小时前
SSH 突然 Connection reset?不是密钥坏了,是 OpenSSL 把 sshd 干趴了
前端·node.js·命令行
程序员大阳5 小时前
副队长ES6+教程(3)--解构赋值
前端·javascript·es6·赋值·解构
神膘护体小月半5 小时前
css - 元素背景图由 hover 触发,初次加载时闪动的解决办法
前端·javascript·css
程序员大阳5 小时前
副队长ES6+教程(2)--let 与 const
前端·javascript·es6·const·let·var
deli0075 小时前
写满 200GB 后 SSD 为什么掉到 95MB/s?SLC 缓存模型实测
前端