perf: flatten children

pull/2510/head
tanjinzhou 2020-06-30 18:31:58 +08:00
parent dd68c0d314
commit 7eeee692e7
4 changed files with 80 additions and 28 deletions

View File

@ -67,12 +67,39 @@ const getSlots = ele => {
});
return { ...slots, ...getScopedSlots(ele) };
};
const getSlot = (self, name = 'default', options = {}) => {
let res = self.$slots[name] && self.$slots[name](options);
while (res && res.length === 1 && (res[0].type === Fragment || Array.isArray(res[0]))) {
res = res[0].children || res[0];
const flattenChildren = (children = [], filterEmpty = true) => {
const temp = Array.isArray(children) ? children : [children];
const res = [];
temp.forEach(child => {
if (Array.isArray(child)) {
res.push(...flattenChildren(child, filterEmpty));
} else if (child && child.type === Fragment) {
res.push(...flattenChildren(child.children, filterEmpty));
} else if (child && isVNode(child)) {
if (filterEmpty && !isEmptyElement(child)) {
res.push(child);
} else if (!filterEmpty) {
res.push(child);
}
}
});
return res;
};
const getSlot = (self, name = 'default', options = {}) => {
if (isVNode(self)) {
if (self.type === Fragment) {
return name === 'default' ? flattenChildren(self.children) : [];
} else if (self.children && self.children[name]) {
return flattenChildren(self.children[name](options));
} else {
return [];
}
} else {
let res = self.$slots[name] && self.$slots[name](options);
return flattenChildren(res);
}
return res && res.__v_isVNode ? [res] : res;
};
const getAllChildren = ele => {
@ -128,26 +155,32 @@ const getOptionProps = instance => {
return res;
};
const getComponent = (instance, prop = 'default', options = instance, execute = true) => {
let com = undefined;
if (instance.$) {
const temp = instance[prop];
if (temp !== undefined) {
return typeof temp === 'function' && execute ? temp(options) : temp;
} else {
let com = instance.$slots[prop];
com = instance.$slots[prop];
com = execute && com ? com(options) : com;
return Array.isArray(com) && com.length === 1 ? com[0] : com;
}
} else if (isVNode(instance)) {
const temp = instance.props && instance.props[prop];
if (temp !== undefined && temp !== null) {
if (temp !== undefined && instance.props !== null) {
return typeof temp === 'function' && execute ? temp(options) : temp;
} else if (instance.type === Fragment) {
com = instance.children;
} else if (instance.children && instance.children[prop]) {
let com = instance.children[prop];
com = instance.children[prop];
com = execute && com ? com(options) : com;
return Array.isArray(com) && com.length === 1 ? com[0] : com;
}
}
return undefined;
if (Array.isArray(com)) {
com = flattenChildren(com);
com = com.length === 1 ? com[0] : com;
com = com.length === 0 ? undefined : com;
}
return com;
};
const getComponentFromProp = (instance, prop, options = instance, execute = true) => {
if (instance.$createElement) {
@ -381,5 +414,6 @@ export {
getAllProps,
getAllChildren,
findDOMNode,
flattenChildren,
};
export default hasProp;

View File

@ -5,7 +5,7 @@ import scrollIntoView from 'dom-scroll-into-view';
import { getSelectKeys, preventDefaultEvent, saveRef } from './util';
import { cloneElement } from '../_util/vnode';
import BaseMixin from '../_util/BaseMixin';
import { findDOMNode } from '../_util/props-util';
import { findDOMNode, getSlot } from '../_util/props-util';
export default {
name: 'DropdownMenu',
@ -152,7 +152,7 @@ export default {
clonedMenuItems = menuItems.map(item => {
if (item.type.isMenuItemGroup) {
const children = (item.children?.default() || []).map(clone);
const children = getSlot(item).map(clone);
const newItem = cloneElement(item);
newItem.children = { ...item.children, default: () => children };
return newItem;

View File

@ -14,6 +14,7 @@ import {
getComponent,
getEvents,
getOptionProps,
getSlot,
} from '../_util/props-util';
import getTransitionProps from '../_util/getTransitionProps';
import { cloneElement } from '../_util/vnode';
@ -223,11 +224,11 @@ const Select = {
},
getOptionsFromChildren(children = [], options = []) {
children.forEach(child => {
if (!child.data || child.data.slot !== undefined) {
if (!child) {
return;
}
if (child.type?.isSelectOptGroup) {
this.getOptionsFromChildren(child.children?.default(), options);
this.getOptionsFromChildren(getSlot(child), options);
} else {
options.push(child);
}
@ -782,7 +783,7 @@ const Select = {
const props = this.$props;
const { _inputValue: inputValue, _mirrorInputValue } = this.$data;
const attrs = this.$attrs;
const defaultInput = <input id={attrs.id} autoComplete="off" />;
const defaultInput = <input {...(attrs.id !== undefined ? {id: attrs.id}: {})} autoComplete="off"/>;
const inputElement = props.getInputElement ? props.getInputElement() : defaultInput;
const inputCls = classnames(inputElement.class, {
@ -795,7 +796,6 @@ const Select = {
<div class={`${props.prefixCls}-search__field__wrap`} onClick={this.inputClick}>
{cloneElement(inputElement, {
disabled: props.disabled,
value: inputValue,
...(inputElement.props || {}),
disabled: props.disabled,
value: inputValue,
@ -881,6 +881,21 @@ const Select = {
if (fireSearch) {
this.$emit('search', inputValue);
}
} else {
// TODO
// https://github.com/vuejs/vue-next/issues/1471
this.setState(
{
_inputValue: `${inputValue} `,
},
);
this.$nextTick(()=>{
this.setState(
{
_inputValue: inputValue,
},
);
});
}
},
getValueByInput(str) {
@ -1200,7 +1215,7 @@ const Select = {
const childValueSub = getValuePropValue(subChild) || subChild.key;
return (
<MenuItem key={childValueSub} value={childValueSub} {...subChild.props}>
{...(subChild.children?.default())}
{...getSlot(subChild)}
</MenuItem>
);
});
@ -1247,7 +1262,7 @@ const Select = {
style: UNSELECTABLE_STYLE,
class: child?.class,
};
const menuItem = <MenuItem {...p}>{child.children?.default()}</MenuItem>;
const menuItem = <MenuItem {...p}>{getSlot(child)}</MenuItem>;
sel.push(menuItem);
menuItems.push(menuItem);
}
@ -1503,14 +1518,17 @@ const Select = {
}
}
},
selectionRefFocus(e) {
if (this._focused || this.disabled || isMultipleOrTagsOrCombobox(this.$props)) {
e.preventDefault();
return;
selectionRefFocus() {
if (this.getInputDOMNode() && this.getInputDOMNode()) {
this.getInputDOMNode().focus();
}
this._focused = true;
this.updateFocusClassName();
this.$emit('focus');
// if (this._focused || this.disabled || isMultipleOrTagsOrCombobox(this.$props)) {
// e.preventDefault();
// return;
// }
// this._focused = true;
// this.updateFocusClassName();
// this.$emit('focus');
},
selectionRefBlur(e) {
if (isMultipleOrTagsOrCombobox(this.$props)) {

View File

@ -1,4 +1,4 @@
import { getPropsData, getComponent } from '../_util/props-util';
import { getPropsData, getComponent, getSlot } from '../_util/props-util';
import { cloneElement } from '../_util/vnode';
import { isVNode, Text } from 'vue';
@ -115,7 +115,7 @@ export function getSelectKeys(menuItems = [], value) {
let selectedKeys = [];
menuItems.forEach(item => {
if (item.type?.isMenuItemGroup) {
selectedKeys = selectedKeys.concat(getSelectKeys(item.children?.default(), value));
selectedKeys = selectedKeys.concat(getSelectKeys(getSlot(item), value));
} else {
const itemValue = getValuePropValue(item);
const itemKey = item.key;
@ -141,7 +141,7 @@ export function findFirstMenuItem(children) {
const child = children[i];
const props = getPropsData(child);
if (child.type?.isMenuItemGroup) {
const found = findFirstMenuItem(child.children?.default());
const found = findFirstMenuItem(getSlot(child));
if (found) {
return found;
}