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

View File

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

View File

@ -14,6 +14,7 @@ import {
getComponent, getComponent,
getEvents, getEvents,
getOptionProps, getOptionProps,
getSlot,
} from '../_util/props-util'; } from '../_util/props-util';
import getTransitionProps from '../_util/getTransitionProps'; import getTransitionProps from '../_util/getTransitionProps';
import { cloneElement } from '../_util/vnode'; import { cloneElement } from '../_util/vnode';
@ -223,11 +224,11 @@ const Select = {
}, },
getOptionsFromChildren(children = [], options = []) { getOptionsFromChildren(children = [], options = []) {
children.forEach(child => { children.forEach(child => {
if (!child.data || child.data.slot !== undefined) { if (!child) {
return; return;
} }
if (child.type?.isSelectOptGroup) { if (child.type?.isSelectOptGroup) {
this.getOptionsFromChildren(child.children?.default(), options); this.getOptionsFromChildren(getSlot(child), options);
} else { } else {
options.push(child); options.push(child);
} }
@ -782,7 +783,7 @@ const Select = {
const props = this.$props; const props = this.$props;
const { _inputValue: inputValue, _mirrorInputValue } = this.$data; const { _inputValue: inputValue, _mirrorInputValue } = this.$data;
const attrs = this.$attrs; 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 inputElement = props.getInputElement ? props.getInputElement() : defaultInput;
const inputCls = classnames(inputElement.class, { const inputCls = classnames(inputElement.class, {
@ -795,7 +796,6 @@ const Select = {
<div class={`${props.prefixCls}-search__field__wrap`} onClick={this.inputClick}> <div class={`${props.prefixCls}-search__field__wrap`} onClick={this.inputClick}>
{cloneElement(inputElement, { {cloneElement(inputElement, {
disabled: props.disabled, disabled: props.disabled,
value: inputValue,
...(inputElement.props || {}), ...(inputElement.props || {}),
disabled: props.disabled, disabled: props.disabled,
value: inputValue, value: inputValue,
@ -881,6 +881,21 @@ const Select = {
if (fireSearch) { if (fireSearch) {
this.$emit('search', inputValue); 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) { getValueByInput(str) {
@ -1200,7 +1215,7 @@ const Select = {
const childValueSub = getValuePropValue(subChild) || subChild.key; const childValueSub = getValuePropValue(subChild) || subChild.key;
return ( return (
<MenuItem key={childValueSub} value={childValueSub} {...subChild.props}> <MenuItem key={childValueSub} value={childValueSub} {...subChild.props}>
{...(subChild.children?.default())} {...getSlot(subChild)}
</MenuItem> </MenuItem>
); );
}); });
@ -1247,7 +1262,7 @@ const Select = {
style: UNSELECTABLE_STYLE, style: UNSELECTABLE_STYLE,
class: child?.class, class: child?.class,
}; };
const menuItem = <MenuItem {...p}>{child.children?.default()}</MenuItem>; const menuItem = <MenuItem {...p}>{getSlot(child)}</MenuItem>;
sel.push(menuItem); sel.push(menuItem);
menuItems.push(menuItem); menuItems.push(menuItem);
} }
@ -1503,14 +1518,17 @@ const Select = {
} }
} }
}, },
selectionRefFocus(e) { selectionRefFocus() {
if (this._focused || this.disabled || isMultipleOrTagsOrCombobox(this.$props)) { if (this.getInputDOMNode() && this.getInputDOMNode()) {
e.preventDefault(); this.getInputDOMNode().focus();
return;
} }
this._focused = true; // if (this._focused || this.disabled || isMultipleOrTagsOrCombobox(this.$props)) {
this.updateFocusClassName(); // e.preventDefault();
this.$emit('focus'); // return;
// }
// this._focused = true;
// this.updateFocusClassName();
// this.$emit('focus');
}, },
selectionRefBlur(e) { selectionRefBlur(e) {
if (isMultipleOrTagsOrCombobox(this.$props)) { 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 { cloneElement } from '../_util/vnode';
import { isVNode, Text } from 'vue'; import { isVNode, Text } from 'vue';
@ -115,7 +115,7 @@ export function getSelectKeys(menuItems = [], value) {
let selectedKeys = []; let selectedKeys = [];
menuItems.forEach(item => { menuItems.forEach(item => {
if (item.type?.isMenuItemGroup) { if (item.type?.isMenuItemGroup) {
selectedKeys = selectedKeys.concat(getSelectKeys(item.children?.default(), value)); selectedKeys = selectedKeys.concat(getSelectKeys(getSlot(item), value));
} else { } else {
const itemValue = getValuePropValue(item); const itemValue = getValuePropValue(item);
const itemKey = item.key; const itemKey = item.key;
@ -141,7 +141,7 @@ export function findFirstMenuItem(children) {
const child = children[i]; const child = children[i];
const props = getPropsData(child); const props = getPropsData(child);
if (child.type?.isMenuItemGroup) { if (child.type?.isMenuItemGroup) {
const found = findFirstMenuItem(child.children?.default()); const found = findFirstMenuItem(getSlot(child));
if (found) { if (found) {
return found; return found;
} }