Skip to content

Commit 7127a5d

Browse files
committed
perf: tree & treeselect vueComponent#5365
1 parent 96f5081 commit 7127a5d

15 files changed

+222
-109
lines changed

components/table/hooks/useSelection.tsx

+9-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import type {
2828
ExpandType,
2929
GetPopupContainer,
3030
} from '../interface';
31+
import useMaxLevel from '../../vc-tree/useMaxLevel';
3132

3233
// TODO: warning if use ajax!!!
3334

@@ -120,7 +121,7 @@ export default function useSelection<RecordType>(
120121

121122
const keyEntities = computed(() =>
122123
mergedRowSelection.value.checkStrictly
123-
? { keyEntities: null }
124+
? null
124125
: convertDataToEntities(configRef.data.value as unknown as DataNode[], {
125126
externalGetKey: configRef.getRowKey.value as any,
126127
childrenPropName: configRef.childrenColumnName.value,
@@ -155,7 +156,7 @@ export default function useSelection<RecordType>(
155156
});
156157
return map;
157158
});
158-
159+
const { maxLevel, levelEntities } = useMaxLevel(keyEntities);
159160
const isCheckboxDisabled: GetCheckDisabled<RecordType> = (r: RecordType) =>
160161
!!checkboxPropsMap.value.get(configRef.getRowKey.value(r))?.disabled;
161162

@@ -167,6 +168,8 @@ export default function useSelection<RecordType>(
167168
mergedSelectedKeys.value,
168169
true,
169170
keyEntities.value,
171+
maxLevel.value,
172+
levelEntities.value,
170173
isCheckboxDisabled as any,
171174
);
172175
return [checkedKeys || [], halfCheckedKeys];
@@ -571,6 +574,8 @@ export default function useSelection<RecordType>(
571574
[...originCheckedKeys, key],
572575
true,
573576
keyEntities.value,
577+
maxLevel.value,
578+
levelEntities.value,
574579
isCheckboxDisabled as any,
575580
);
576581
const { checkedKeys, halfCheckedKeys } = result;
@@ -584,6 +589,8 @@ export default function useSelection<RecordType>(
584589
Array.from(tempKeySet),
585590
{ checked: false, halfCheckedKeys },
586591
keyEntities.value,
592+
maxLevel.value,
593+
levelEntities.value,
587594
isCheckboxDisabled as any,
588595
).checkedKeys;
589596
}

components/vc-cascader/Cascader.tsx

+19-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { useProvideCascader } from './context';
2121
import OptionList from './OptionList';
2222
import { BaseSelect } from '../vc-select';
2323
import devWarning from '../vc-util/devWarning';
24+
import useMaxLevel from '../vc-tree/useMaxLevel';
2425

2526
export interface ShowSearchType<OptionType extends BaseOptionType = DefaultOptionType> {
2627
filter?: (inputValue: string, options: OptionType[], fieldNames: FieldNames) => boolean;
@@ -259,6 +260,8 @@ export default defineComponent({
259260
ref<SingleValueType[]>([]),
260261
ref<SingleValueType[]>([]),
261262
];
263+
264+
const { maxLevel, levelEntities } = useMaxLevel(pathKeyEntities);
262265
watchEffect(() => {
263266
const [existValues, missingValues] = missingValuesInfo.value;
264267

@@ -274,7 +277,13 @@ export default defineComponent({
274277
const keyPathValues = toPathKeys(existValues);
275278
const ketPathEntities = pathKeyEntities.value;
276279

277-
const { checkedKeys, halfCheckedKeys } = conductCheck(keyPathValues, true, ketPathEntities);
280+
const { checkedKeys, halfCheckedKeys } = conductCheck(
281+
keyPathValues,
282+
true,
283+
ketPathEntities,
284+
maxLevel.value,
285+
levelEntities.value,
286+
);
278287

279288
// Convert key back to value cells
280289
[checkedValues.value, halfCheckedValues.value, missingCheckedValues.value] = [
@@ -356,9 +365,17 @@ export default defineComponent({
356365
nextRawCheckedKeys,
357366
{ checked: false, halfCheckedKeys: halfCheckedPathKeys },
358367
pathKeyEntities.value,
368+
maxLevel.value,
369+
levelEntities.value,
359370
));
360371
} else {
361-
({ checkedKeys } = conductCheck(nextRawCheckedKeys, true, pathKeyEntities.value));
372+
({ checkedKeys } = conductCheck(
373+
nextRawCheckedKeys,
374+
true,
375+
pathKeyEntities.value,
376+
maxLevel.value,
377+
levelEntities.value,
378+
));
362379
}
363380

364381
// Roll up to parent level keys

components/vc-select/OptionList.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import classNames from '../_util/classNames';
55
import pickAttrs from '../_util/pickAttrs';
66
import { isValidElement } from '../_util/props-util';
77
import createRef from '../_util/createRef';
8-
import { computed, defineComponent, nextTick, reactive, watch } from 'vue';
8+
import { computed, defineComponent, nextTick, reactive, toRaw, watch } from 'vue';
99
import List from '../vc-virtual-list';
1010
import useMemo from '../_util/hooks/useMemo';
1111
import { isPlatformMac } from './utils/platformUtil';
@@ -105,7 +105,9 @@ const OptionList = defineComponent({
105105
() => {
106106
if (!baseProps.multiple && baseProps.open && props.rawValues.size === 1) {
107107
const value = Array.from(props.rawValues)[0];
108-
const index = memoFlattenOptions.value.findIndex(({ data }) => data.value === value);
108+
const index = toRaw(memoFlattenOptions.value).findIndex(
109+
({ data }) => data.value === value,
110+
);
109111
if (index !== -1) {
110112
setActive(index);
111113
nextTick(() => {

components/vc-select/hooks/useFilterOptions.ts

+14-13
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import type {
88
} from '../Select';
99
import { injectPropsWithOption } from '../utils/valueUtil';
1010
import type { Ref } from 'vue';
11-
import { computed } from 'vue';
11+
import { toRaw, computed } from 'vue';
1212

1313
function includes(test: any, search: string) {
1414
return toArray(test).join('').toUpperCase().includes(search);
@@ -22,22 +22,24 @@ export default (
2222
optionFilterProp?: Ref<string>,
2323
) =>
2424
computed(() => {
25-
if (!searchValue.value || filterOption.value === false) {
25+
const searchValueVal = searchValue.value;
26+
const optionFilterPropValue = optionFilterProp?.value;
27+
const filterOptionValue = filterOption?.value;
28+
if (!searchValueVal || filterOptionValue === false) {
2629
return options.value;
2730
}
28-
2931
const { options: fieldOptions, label: fieldLabel, value: fieldValue } = fieldNames.value;
3032
const filteredOptions: DefaultOptionType[] = [];
3133

32-
const customizeFilter = typeof filterOption.value === 'function';
34+
const customizeFilter = typeof filterOptionValue === 'function';
3335

34-
const upperSearch = searchValue.value.toUpperCase();
36+
const upperSearch = searchValueVal.toUpperCase();
3537
const filterFunc = customizeFilter
36-
? (filterOption.value as FilterFunc<BaseOptionType>)
38+
? (filterOptionValue as FilterFunc<BaseOptionType>)
3739
: (_: string, option: DefaultOptionType) => {
3840
// Use provided `optionFilterProp`
39-
if (optionFilterProp.value) {
40-
return includes(option[optionFilterProp.value], upperSearch);
41+
if (optionFilterPropValue) {
42+
return includes(option[optionFilterPropValue], upperSearch);
4143
}
4244

4345
// Auto select `label` or `value` by option type
@@ -53,17 +55,17 @@ export default (
5355
? opt => injectPropsWithOption(opt)
5456
: opt => opt;
5557

56-
options.value.forEach(item => {
58+
toRaw(options.value).forEach(item => {
5759
// Group should check child options
5860
if (item[fieldOptions]) {
5961
// Check group first
60-
const matchGroup = filterFunc(searchValue.value, wrapOption(item));
62+
const matchGroup = filterFunc(searchValueVal, wrapOption(item));
6163
if (matchGroup) {
6264
filteredOptions.push(item);
6365
} else {
6466
// Check option
6567
const subOptions = item[fieldOptions].filter((subItem: DefaultOptionType) =>
66-
filterFunc(searchValue.value, wrapOption(subItem)),
68+
filterFunc(searchValueVal, wrapOption(subItem)),
6769
);
6870
if (subOptions.length) {
6971
filteredOptions.push({
@@ -76,10 +78,9 @@ export default (
7678
return;
7779
}
7880

79-
if (filterFunc(searchValue.value, wrapOption(item))) {
81+
if (filterFunc(searchValueVal, wrapOption(item))) {
8082
filteredOptions.push(item);
8183
}
8284
});
83-
8485
return filteredOptions;
8586
});

components/vc-select/hooks/useOptions.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Ref } from 'vue';
2-
import { shallowRef, watchEffect } from 'vue';
2+
import { toRaw, shallowRef, watchEffect } from 'vue';
33
import type { FieldNames, RawValueType } from '../Select';
44
import { convertChildrenToData } from '../utils/legacyUtil';
55

@@ -16,7 +16,7 @@ export default function useOptions<OptionType>(
1616
const valueOptions = shallowRef();
1717
const labelOptions = shallowRef();
1818
watchEffect(() => {
19-
let newOptions = options.value;
19+
let newOptions = toRaw(options.value);
2020
const childrenAsData = !options.value;
2121

2222
if (childrenAsData) {
@@ -25,16 +25,16 @@ export default function useOptions<OptionType>(
2525

2626
const newValueOptions = new Map<RawValueType, OptionType>();
2727
const newLabelOptions = new Map<any, OptionType>();
28-
28+
const fieldNamesValue = fieldNames.value;
2929
function dig(optionList: OptionType[], isChildren = false) {
3030
// for loop to speed up collection speed
3131
for (let i = 0; i < optionList.length; i += 1) {
3232
const option = optionList[i];
33-
if (!option[fieldNames.value.options] || isChildren) {
34-
newValueOptions.set(option[fieldNames.value.value], option);
35-
newLabelOptions.set(option[fieldNames.value.label], option);
33+
if (!option[fieldNamesValue.options] || isChildren) {
34+
newValueOptions.set(option[fieldNamesValue.value], option);
35+
newLabelOptions.set(option[fieldNamesValue.label], option);
3636
} else {
37-
dig(option[fieldNames.value.options], true);
37+
dig(option[fieldNamesValue.options], true);
3838
}
3939
}
4040
}

components/vc-tree-select/OptionList.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { TreeDataNode, Key } from './interface';
22
import type { RefOptionListProps } from '../vc-select/OptionList';
33
import type { ScrollTo } from '../vc-virtual-list/List';
4-
import { computed, defineComponent, nextTick, ref, shallowRef, watch } from 'vue';
4+
import { computed, defineComponent, nextTick, ref, shallowRef, toRaw, watch } from 'vue';
55
import useMemo from '../_util/hooks/useMemo';
66
import type { EventDataNode } from '../tree';
77
import KeyCode from '../_util/KeyCode';
@@ -89,7 +89,7 @@ export default defineComponent({
8989
() => baseProps.searchValue,
9090
() => {
9191
if (baseProps.searchValue) {
92-
searchExpandedKeys.value = getAllKeys(context.treeData, context.fieldNames);
92+
searchExpandedKeys.value = getAllKeys(toRaw(context.treeData), toRaw(context.fieldNames));
9393
}
9494
},
9595
{
@@ -98,7 +98,7 @@ export default defineComponent({
9898
);
9999
const mergedExpandedKeys = computed(() => {
100100
if (legacyContext.treeExpandedKeys) {
101-
return [...legacyContext.treeExpandedKeys];
101+
return toRaw(legacyContext.treeExpandedKeys).slice();
102102
}
103103
return baseProps.searchValue ? searchExpandedKeys.value : expandedKeys.value;
104104
});

components/vc-tree-select/TreeSelect.tsx

+19-6
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import type { VueNode } from '../_util/type';
2929
import { conductCheck } from '../vc-tree/utils/conductUtil';
3030
import { warning } from '../vc-util/warning';
3131
import { toReactive } from '../_util/toReactive';
32+
import useMaxLevel from '../vc-tree/useMaxLevel';
3233

3334
export type OnInternalSelect = (value: RawValueType, info: { selected: boolean }) => void;
3435

@@ -364,13 +365,15 @@ export default defineComponent({
364365

365366
// const [mergedValues] = useCache(rawLabeledValues);
366367
const rawValues = computed(() => rawLabeledValues.value.map(item => item.value));
367-
368+
const { maxLevel, levelEntities } = useMaxLevel(keyEntities);
368369
// Convert value to key. Will fill missed keys for conduct check.
369370
const [rawCheckedValues, rawHalfCheckedValues] = useCheckedKeys(
370371
rawLabeledValues,
371372
rawHalfLabeledValues,
372373
treeConduction,
373374
keyEntities,
375+
maxLevel,
376+
levelEntities,
374377
);
375378

376379
// Convert rawCheckedKeys to check strategy related values
@@ -504,7 +507,9 @@ export default defineComponent({
504507
selectedKey: Key,
505508
{ selected, source }: { selected: boolean; source: SelectSource },
506509
) => {
507-
const entity = keyEntities.value[selectedKey];
510+
const keyEntitiesValue = toRaw(keyEntities.value);
511+
const valueEntitiesValue = toRaw(valueEntities.value);
512+
const entity = keyEntitiesValue[selectedKey];
508513
const node = entity?.node;
509514
const selectedValue = node?.[mergedFieldNames.value.value] ?? selectedKey;
510515

@@ -521,24 +526,32 @@ export default defineComponent({
521526
if (treeConduction.value) {
522527
// Should keep missing values
523528
const { missingRawValues, existRawValues } = splitRawValues(newRawValues);
524-
const keyList = existRawValues.map(val => valueEntities.value.get(val).key);
529+
const keyList = existRawValues.map(val => valueEntitiesValue.get(val).key);
525530

526531
// Conduction by selected or not
527532
let checkedKeys: Key[];
528533
if (selected) {
529-
({ checkedKeys } = conductCheck(keyList, true, keyEntities.value));
534+
({ checkedKeys } = conductCheck(
535+
keyList,
536+
true,
537+
keyEntitiesValue,
538+
maxLevel.value,
539+
levelEntities.value,
540+
));
530541
} else {
531542
({ checkedKeys } = conductCheck(
532543
keyList,
533544
{ checked: false, halfCheckedKeys: rawHalfCheckedValues.value },
534-
keyEntities.value,
545+
keyEntitiesValue,
546+
maxLevel.value,
547+
levelEntities.value,
535548
));
536549
}
537550

538551
// Fill back of keys
539552
newRawValues = [
540553
...missingRawValues,
541-
...checkedKeys.map(key => keyEntities.value[key].node[mergedFieldNames.value.value]),
554+
...checkedKeys.map(key => keyEntitiesValue[key].node[mergedFieldNames.value.value]),
542555
];
543556
}
544557
triggerChange(newRawValues, { selected, triggerValue: selectedValue }, source || 'option');

components/vc-tree-select/hooks/useCache.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Ref } from 'vue';
2-
import { computed, shallowRef } from 'vue';
2+
import { toRaw, computed, shallowRef } from 'vue';
33
import type { LabeledValueType, RawValueType } from '../TreeSelect';
44

55
/**
@@ -15,7 +15,7 @@ export default (values: Ref<LabeledValueType[]>): [Ref<LabeledValueType[]>] => {
1515
const { valueLabels } = cacheRef.value;
1616
const valueLabelsCache = new Map<RawValueType, any>();
1717

18-
const filledValues = values.value.map(item => {
18+
const filledValues = toRaw(values.value).map(item => {
1919
const { value } = item;
2020
const mergedLabel = item.label ?? valueLabels.get(value);
2121

components/vc-tree-select/hooks/useCheckedKeys.ts

+15-5
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,36 @@ import type { Key } from '../../_util/type';
22
import type { DataEntity } from '../../vc-tree/interface';
33
import { conductCheck } from '../../vc-tree/utils/conductUtil';
44
import type { LabeledValueType, RawValueType } from '../TreeSelect';
5-
import type { Ref } from 'vue';
6-
import { shallowRef, watchEffect } from 'vue';
5+
import type { Ref, ShallowRef } from 'vue';
6+
import { toRaw, shallowRef, watchEffect } from 'vue';
77

88
export default (
99
rawLabeledValues: Ref<LabeledValueType[]>,
1010
rawHalfCheckedValues: Ref<LabeledValueType[]>,
1111
treeConduction: Ref<boolean>,
1212
keyEntities: Ref<Record<Key, DataEntity>>,
13+
maxLevel: Ref<number>,
14+
levelEntities: ShallowRef<Map<number, Set<DataEntity>>>,
1315
) => {
1416
const newRawCheckedValues = shallowRef<RawValueType[]>([]);
1517
const newRawHalfCheckedValues = shallowRef<RawValueType[]>([]);
1618

1719
watchEffect(() => {
18-
let checkedKeys: RawValueType[] = rawLabeledValues.value.map(({ value }) => value);
19-
let halfCheckedKeys: RawValueType[] = rawHalfCheckedValues.value.map(({ value }) => value);
20+
let checkedKeys: RawValueType[] = toRaw(rawLabeledValues.value).map(({ value }) => value);
21+
let halfCheckedKeys: RawValueType[] = toRaw(rawHalfCheckedValues.value).map(
22+
({ value }) => value,
23+
);
2024

2125
const missingValues = checkedKeys.filter(key => !keyEntities.value[key]);
2226

2327
if (treeConduction.value) {
24-
({ checkedKeys, halfCheckedKeys } = conductCheck(checkedKeys, true, keyEntities.value));
28+
({ checkedKeys, halfCheckedKeys } = conductCheck(
29+
checkedKeys,
30+
true,
31+
keyEntities.value,
32+
maxLevel.value,
33+
levelEntities.value,
34+
));
2535
}
2636
newRawCheckedValues.value = Array.from(new Set([...missingValues, ...checkedKeys]));
2737
newRawHalfCheckedValues.value = halfCheckedKeys;

0 commit comments

Comments
 (0)