问题描述
自有项目中需要实现一个collapse折叠面板的功能,由于对饿了么使用较多,故打算将饿了么中的collapse组件移植到项目中,只需要对一些细节的样式进行修改即可。本着兢(混)兢(水)业(摸)业(鱼)的态度,开始移植collapse折叠面板。
但是在查看饿了么的源码,以及实现了collapse功能后,发现面板的动画效果失效了,打开折叠的操作感觉很生硬。影响使用体验。
经过细致的排查,发现问题出现在 el-collapse-transition
组件上,通过断点发现,改组件的render有加载,但是class Transition完全不会被呼叫,即class Transition里定义的各种方法没有被触发,所以导致el-collapse-transition
失效。
思路解析
问题出现在 el-collapse-transition
组件上,但是移植时代码已饿了么的源码一致,实现方式是通过vue的函数式组件的方式来实现的,对于函数式组件也不是很了解。多方查找原因,发现出在这个原来是ES6中class类的原型是不可以在vue中通过v-for遍历的。可以将ES6的class类的写法换成ES5的写法,ES5中class类的原型是可以进行遍历的。
new 出来的实例 的各种方法 是挂在新对象的原型上, 但是on 里面的函数必须是要在新对象本身上。
本着这个想法,看了饿了么npm包中transition文件夹下collapse-transition.js文件,果然写法转换为了ES5的写法。应该是饿了么发布npm时通过babel转换了写法。
解决方案
方法一
简单一些,可以直接将elementUI的npm包中的源码直接拿过来,可以正常运行,也不需要自己在进行其他的改写。
注意的一点是对dom.js的引入,文件位置的变化。
附上饿了么转译后的代码:
'use strict';
exports.__esModule = true;
var _dom = require('element-ui/lib/utils/dom');
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var Transition = function () {
function Transition() {
_classCallCheck(this, Transition);
}
Transition.prototype.beforeEnter = function beforeEnter(el) {
(0, _dom.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;
};
Transition.prototype.enter = function 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';
};
Transition.prototype.afterEnter = function afterEnter(el) {
(0, _dom.removeClass)(el, 'collapse-transition');
el.style.height = '';
el.style.overflow = el.dataset.oldOverflow;
};
Transition.prototype.beforeLeave = function 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';
};
Transition.prototype.leave = function leave(el) {
if (el.scrollHeight !== 0) {
(0, _dom.addClass)(el, 'collapse-transition');
el.style.height = 0;
el.style.paddingTop = 0;
el.style.paddingBottom = 0;
}
};
Transition.prototype.afterLeave = function afterLeave(el) {
(0, _dom.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;
};
return Transition;
}();
exports.default = {
name: 'ElCollapseTransition',
functional: true,
render: function render(h, _ref) {
var children = _ref.children;
var data = {
on: new Transition()
};
return h('transition', data, children);
}
};
方法二:
需要一定的ES5中class类的实现基础,将原来饿了么源码中的ES6写法仔细改写为ES5的class类实现,限于本人水平有限,放弃了这种方式o(╥﹏╥)o
方法三:
此方法无需改写成ES5写法,需要将transition定义为一个对象(变量),即将:
class Transition {
beforeEnter(el) { ...}
enter(el) { ...}
....
}
改写为:
const Transition = {
beforeEnter(el) { ...},
enter(el) { ...},
....
}
然后在render中将
render: function render(h, _ref) {
var children = _ref.children;
var data = {
on: new Transition()
};
return h('transition', data, children);
}
修改为:
render: function render(h, _ref) {
var children = _ref.children;
var data = {
on: Transition
};
return h('transition', data, children);
}
但是这样写的话,肯定是有问题的, 所有生成的组件都共用了一个对象。当使用较多改组件时,由于共用一个对象,会变得难以维护。
以上即为关于el-collapse-transition
移植遇到问题的解决方法。
tips:
点滴记录,汇聚江河