一天一个 Element 组件 - Collapse
source link: https://shiningdan.github.io/2020/02/16/%E4%B8%80%E5%A4%A9%E4%B8%80%E4%B8%AA-Element-%E7%BB%84%E4%BB%B6-Collapse/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
本文是 Element 的组件源码学习系列。
项目源码:ElemeFE/element | GitHub,Tag:v2.13.0
Collapse 组件使用文档:Tag 标签
.vue 文件:/packages/collapse
.scss 文件:/packages/theme-chalk/collapse.scss
.d.ts 文件:/types/collapse.d.ts
折叠面板,是前端入门的同学,经常手写的入门 Demo,我们现在也来学习一下,Element 是怎么做折叠面板这个组件的实现的。
Props
一样的,咱们先来了解一下 el-collapse
组件的入参,看看用户是如何使用这个组件的:
props: { |
accordion
属性,表示的是,当前折叠面板,是否使用手风琴模式。
value
属性,当前激活的面板(如果是手风琴模式,绑定值类型需要为 string
,否则为array
)。表示的是用户页面上,被激活的面板是哪一个。手风琴模式,只有一个唯一的被激活面板,所以绑定值类型就是一个 string
了。
但是,我们并不是直接给 value
属性赋值,而是使用的 v-model
来利用 value
属性。组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件
。
我们来看一下具体实现:
// collapse.vue |
首先,当我们给 value
属性赋值,或者给 v-model
赋值的时候,都会改变 activeNames
的值。这里比较巧妙的是使用 Array.prototype.concat
方法,因为 value
的类型可能是一个数组,也可能是一个元素,所以直接用 concat
方法统一处理成一个元素。
首先,用户在点击折叠面板的时候,点击的是里层 el-collapse-item
元素,所以我们来看一下,用户在点击打开或关闭一个面板,会有什么相关的逻辑:
// collpase-item.vue |
当这个组件没有设置不可点击的时候,首先会通过 this.dispatch
方法,给父 ElCollapse
组件发出一个 item-click
事件。然后设置两个属性的值。
等等,this.dispatch
是个啥?好像不是原生的 dispatchEvent
属性啊,但是看起来差不多。也没有见到 Vue 提供 dispatch
方法呀。最后,我们在 src/mixins/emitter.js 中找到了该方法的实现,原来是是注册到全局的一个混入方法。我们来看一下具体的实现:
// src/mixins/emitter.js |
其本质方法,就是通过 this.$parent
,层层向上,找对应的组件,然后调用该组件的 $emit
方法,产生一个事件。
在咱们这里,是找到祖先的 ElCollapse
组件,因为 Element 的推荐使用方案里面,el-collapse-item
组件是要被包裹在 el-collpase
组件内部的。然后产生一个 item-click
事件。我们来看看 item-click
事件做了什么:
// collpase.vue |
在 el-collpase
的 created
方法中,设置了组件需要监听 item-click
事件,并且调用 handleItemClick
方法。
handleItemClick
方法,主要是处理 el-collpase-item
事件的入参。大致的逻辑是,将 this.activeNames
设置为正确的值。什么是正确的值呢?
- 对于手风琴模式:如果当前 item 已经是
this.activeNames[0]
(说明已经打开了),则设置为''
(关上),否则将this.activeNames[0]
设置为被点击 item(打开)。 - 对于非手风琴模式:如果当前 item 已经在
this.activeNames
数组中存在了(说明已经打开了),则从数组中过滤(关上);否则添加到数组中(打开)
最后再触发 input
事件 和 change
事件。input
事件是 v-model
要求触发的事件;change
事件是 el-collapse
对外提供的监听事件。
所以,我们对于 el-collpase
组件负责的事情,有一个总结:
- 维护一个折叠面板的总体面板开关状态
- 监听每一个面板的点击事件,计算出最新的总体面板开关状态
- 提供对外的
change
事件
而每个面板 el-collpase-item
,是否显示,在面板组件中自己实现。
el-collpase-item 控制显示效果
下面,我们来看看,el-collpase-item
如何控制自己是否要显示的:
// el-collpase-item |
每一个 el-collpase-item
,都会注入父 el-collpase
组件,然后监听 this.collapse.activeNames
数组,来判断自己是否要显示。所有的 el-collpase-item
组件,都需要提供一个唯一的 name
属性,来表明自己是谁,这样才能计算出哪个面板需要展开。如果没有提供 name
属性,Element 会使用 this._uid
属性来表明每一个组件。this._uid
是 Vue 的一个内置属性,每个 Vue 实例都会有一个递增的id。
现在,el-collpase-item
已经有了 isActive
属性来表示该组件是否应该展示了,下面来看看展示、消失的效果是如何实现的:
// el-collpase-item 的 <template> |
整个 el-collpase-item
主要分两部分,第一部分是 Header,第二部分是会被折叠的主要内容。
Header 部分我就不仔细介绍了,主要是使用 flex 布局,然后最右边的箭头初始化的时候是一个向右的箭头,如果有 is-active
类,箭头就会通过 CSS 旋转能力转为向下 transform: rotate(90deg);
Content 部分,要展示的内容,通过一个 <slot>
被包裹在一个 el-collapse-transition
中。这个 el-collapse-transition
是什么呢?我们可以在 src/transition/collapse-transition.js 中找到。具体的实现多,但主要是实现各个动画接口的效果。所有的动画接口,是复用的 Vue transition
组件的接口,参考 过渡 & 动画 | JavaScript 钩子,也不一一细讲了,
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK