說(shuō)明:本文基于element-ui@2.13.0,源碼詳見element逼龟。
官網(wǎng)collapse折疊面板中有一例子:
對(duì)應(yīng)的源碼:
<el-collapse v-model="activeNames" @change="handleChange">
<el-collapse-item title="一致性 Consistency" name="1">
<div>與現(xiàn)實(shí)生活一致:與現(xiàn)實(shí)生活的流程、邏輯保持一致,遵循用戶習(xí)慣的語(yǔ)言和概念研乒;</div>
<div>在界面中一致:所有的元素和結(jié)構(gòu)需保持一致乏盐,比如:設(shè)計(jì)樣式佳窑、圖標(biāo)和文本、元素的位置等父能。</div>
</el-collapse-item>
<el-collapse-item title="反饋 Feedback" name="2">
<div>......</div>
<div>......</div>
</el-collapse-item>
<el-collapse-item title="效率 Efficiency" name="3">
<div>......</div>
<div>.......</div>
<div>......</div>
</el-collapse-item>
<el-collapse-item title="可控 Controllability" name="4">
<div>......</div>
<div>......</div>
</el-collapse-item>
</el-collapse>
可以看出截圖中每項(xiàng)的標(biāo)題和內(nèi)容區(qū)神凑,來(lái)自于一個(gè)個(gè)的el-collapse-item
,展開面板時(shí),是一個(gè)緩慢展開或收起的效果溉委,深入到packages/collapse/src/collapse-item.vue
鹃唯,其實(shí)就是套了一個(gè)el-collapse-transition
組件:
<el-collapse-transition>
<div
class="el-collapse-item__wrap"
v-show="isActive"
role="tabpanel"
:aria-hidden="!isActive"
:aria-labelledby="`el-collapse-head-${id}`"
:id="`el-collapse-content-${id}`"
>
<div class="el-collapse-item__content">
<slot></slot>
</div>
</div>
</el-collapse-transition>
src/transitions/collapse-transition.js
中定義了vue 函數(shù)式組件el-collapse-transition
,通過vue官方內(nèi)置組件transition
實(shí)現(xiàn)瓣喊,并給出了beforeEnter坡慌、enter、afterEnter藻三、beforeLeave洪橘、leave和afterLeave
的實(shí)現(xiàn)。
import { addClass, removeClass } from 'element-ui/src/utils/dom';
class Transition {
beforeEnter(el) {
addClass(el, 'collapse-transition');
if (!el.dataset) el.dataset = {};
el.dataset.oldPaddingTop = el.style.paddingTop;
el.dataset.oldPaddingBottom = el.style.paddingBottom;
el.style.height = '0';
el.style.paddingTop = 0;
el.style.paddingBottom = 0;
}
enter(el) {
el.dataset.oldOverflow = el.style.overflow;
if (el.scrollHeight !== 0) {
el.style.height = el.scrollHeight + 'px';
el.style.paddingTop = el.dataset.oldPaddingTop;
el.style.paddingBottom = el.dataset.oldPaddingBottom;
} else {
el.style.height = '';
el.style.paddingTop = el.dataset.oldPaddingTop;
el.style.paddingBottom = el.dataset.oldPaddingBottom;
}
el.style.overflow = 'hidden';
}
afterEnter(el) {
// for safari: remove class then reset height is necessary
removeClass(el, 'collapse-transition');
el.style.height = '';
el.style.overflow = el.dataset.oldOverflow;
}
beforeLeave(el) {
if (!el.dataset) el.dataset = {};
el.dataset.oldPaddingTop = el.style.paddingTop;
el.dataset.oldPaddingBottom = el.style.paddingBottom;
el.dataset.oldOverflow = el.style.overflow;
el.style.height = el.scrollHeight + 'px';
el.style.overflow = 'hidden';
}
leave(el) {
if (el.scrollHeight !== 0) {
// for safari: add class after set height, or it will jump to zero height suddenly, weired
addClass(el, 'collapse-transition');
el.style.height = 0;
el.style.paddingTop = 0;
el.style.paddingBottom = 0;
}
}
afterLeave(el) {
removeClass(el, 'collapse-transition');
el.style.height = '';
el.style.overflow = el.dataset.oldOverflow;
el.style.paddingTop = el.dataset.oldPaddingTop;
el.style.paddingBottom = el.dataset.oldPaddingBottom;
}
}
export default {
name: 'ElCollapseTransition',
functional: true,
render(h, { children }) {
const data = {
on: new Transition()
};
return h('transition', data, children);
}
};
另外棵帽,
順便提一下el-collapse
組件中手風(fēng)琴(accordion
)效果的實(shí)現(xiàn)方式熄求,所謂手風(fēng)琴就是每次只能展開一個(gè)面板(默認(rèn)多個(gè)面板都可以打開,沒有互斥):
<el-collapse v-model="activeNames" @change="handleChange" accordion>
<el-collapse-item title="一致性 Consistency" name="1"></el-collapse-item>
<el-collapse-item title="反饋 Feedback" name="2"></el-collapse-item>
<el-collapse-item title="效率 Efficiency" name="3"></el-collapse-item>
<el-collapse-item title="可控 Controllability" name="4"></el-collapse-item>
</el-collapse>
- 子組件el-collapse-item中點(diǎn)擊展開時(shí)觸發(fā)點(diǎn)擊方法逗概,核心就是執(zhí)行了
this.dispatch('ElCollapse', 'item-click', this)
弟晚,在dispatch
方法中
通過向上不斷尋找組件名為ElCollapse的組件,并在該組件實(shí)例中觸發(fā)item-click
事件逾苫; - 然后卿城,父組件(也就是
ElCollapse
),通過this.$on('item-click', this.handleItemClick)
方法隶垮,監(jiān)控到item-click
事件藻雪,觸發(fā)handleItemClick
; -
handleItemClick
代碼如下,簡(jiǎn)單說(shuō)就是activeNames
數(shù)組中當(dāng)?shù)谝粋€(gè)元素存在狸吞,如果值跟(通過上一步dispatch傳過來(lái)的)子組件的name
prop一致勉耀,那么清空(展開 -> 關(guān)閉的意思),否則activeNames[0]
賦值為子組件的name
(關(guān)閉 ->展開的意思)蹋偏;
setActiveNames(activeNames) {
activeNames = [].concat(activeNames);
let value = this.accordion ? activeNames[0] : activeNames;
this.activeNames = activeNames;
this.$emit('input', value);
this.$emit('change', value);
}
handleItemClick(item) {
if (this.accordion) {
this.setActiveNames(
(this.activeNames[0] || this.activeNames[0] === 0) &&
this.activeNames[0] === item.name
? '' : item.name
);
}
......
}
- 第3步更新完
activeNames
后便斥,各子組件獲取到父組件的實(shí)例(見官方provide-inject
),并計(jì)算自身的name
prop是否在activeNames
數(shù)組中威始,即計(jì)算屬性isActive
枢纠,最后在模板中通過v-show="isActive"
方式,控制內(nèi)容區(qū)域的展開/收起黎棠。
// collapse
provide() {
return {
collapse: this // 將自身實(shí)例傳給子組件
};
}
// 子組件collapse-item
computed: {
isActive() {
return this.collapse.activeNames.indexOf(this.name) > -1;
}
}
推薦
dispatch晋渺、broadcast的實(shí)現(xiàn)方式
ElementUI的結(jié)構(gòu)與源碼研究
elementUI——locale,國(guó)際化方案
elementUI——directives:mousewheel & repeat-click
elementUI——主題