perf(components): [tabs] improve order performance

This commit is contained in:
三咲智子 2022-09-16 22:10:31 +08:00
parent 116c1daf05
commit ab19e8f2e6
4 changed files with 51 additions and 40 deletions

View File

@ -30,7 +30,7 @@ import { useMenuCssVar } from './use-menu-css-var'
import type { MenuItemClicked, MenuProvider, SubMenuProvider } from './types'
import type { NavigationFailure, Router } from 'vue-router'
import type { ExtractPropTypes, VNode } from 'vue'
import type { ExtractPropTypes, VNode, VNodeArrayChildren } from 'vue'
import type { UseResizeObserverReturn } from '@vueuse/core'
export const menuProps = buildProps({
@ -358,11 +358,11 @@ export default defineComponent({
}
return () => {
let slot = slots.default?.() ?? []
let slot: VNodeArrayChildren = slots.default?.() ?? []
const vShowMore: VNode[] = []
if (props.mode === 'horizontal' && menu.value) {
const originalSlot = flattedChildren(slot)
const originalSlot = flattedChildren(slot) as VNodeArrayChildren
const slotDefault =
sliceIndex.value === -1
? originalSlot

View File

@ -4,16 +4,15 @@ import {
getCurrentInstance,
nextTick,
provide,
reactive,
ref,
renderSlot,
shallowReactive,
shallowRef,
watch,
} from 'vue'
import {
buildProps,
definePropType,
flattedChildren,
getFirstValidNode,
isNumber,
isString,
isUndefined,
@ -24,10 +23,11 @@ import { Plus } from '@element-plus/icons-vue'
import { tabsRootContextKey } from '@element-plus/tokens'
import { useDeprecated, useNamespace } from '@element-plus/hooks'
import TabNav from './tab-nav'
import { getOrderedPanes } from './utils/pane'
import type { TabNavInstance } from './tab-nav'
import type { TabsPaneContext } from '@element-plus/tokens'
import type { ExtractPropTypes, VNode, VNodeNormalizedChildren } from 'vue'
import type { ExtractPropTypes } from 'vue'
import type { Awaitable } from '@element-plus/utils'
export type TabPanelName = string | number
@ -79,6 +79,8 @@ export const tabsEmits = {
}
export type TabsEmits = typeof tabsEmits
export type TabsPanes = Record<number, TabsPaneContext>
export default defineComponent({
name: 'ElTabs',
@ -91,8 +93,8 @@ export default defineComponent({
const ns = useNamespace('tabs')
const nav$ = ref<TabNavInstance>()
const panes = reactive<Record<number, TabsPaneContext>>({})
const orderedPanes = ref<Array<TabsPaneContext>>([])
const panes = shallowReactive<TabsPanes>({})
const orderedPanes = shallowRef<TabsPaneContext[]>([])
const currentName = ref<TabPanelName>(
props.modelValue ?? props.activeName ?? '0'
)
@ -172,39 +174,15 @@ export default defineComponent({
nav$.value?.scrollToActiveTab()
})
// panes-order control
{
const calcOrderedPanes = () => {
const tabContentChildren = getTabContentChildren()
orderedPanes.value = tabContentChildren.map(
(node) => panes[node.component.uid]
)
}
const getTabContentChildren = () => {
const node = vm.subTree
if (Array.isArray(node?.children)) {
const nodeTabContent = node.children.find((x) => {
return (x as VNode)?.props?.class === ns.e('content')
})
const validNodeTabContent = getFirstValidNode(
nodeTabContent as VNodeNormalizedChildren
)
const nodePanes = flattedChildren(
validNodeTabContent as VNodeNormalizedChildren
)
return nodePanes
}
return []
}
const registerPane = (pane: TabsPaneContext) => {
panes[pane.uid] = pane
calcOrderedPanes()
orderedPanes.value = getOrderedPanes(vm, panes)
}
const unregisterPane = (uid: number) => {
delete panes[uid]
calcOrderedPanes()
orderedPanes.value = getOrderedPanes(vm, panes)
}
provide(tabsRootContextKey, {

View File

@ -0,0 +1,20 @@
import { flattedChildren, isVNode } from '@element-plus/utils'
import type { ComponentInternalInstance, VNode } from 'vue'
import type { TabsPanes } from '../tabs'
export const getTabPanes = (vm: ComponentInternalInstance) => {
const nodes = flattedChildren(vm.subTree)
return nodes.filter(
(n): n is VNode =>
isVNode(n) && (n.type as any)?.name === 'ElTabPane' && !!n.component
)
}
export const getOrderedPanes = (
vm: ComponentInternalInstance,
panes: TabsPanes
) => {
const nodes = getTabPanes(vm)
const uids = nodes.map((n) => n.component!.uid)
return uids.map((uid) => panes[uid]).filter((p) => !!p)
}

View File

@ -35,6 +35,12 @@ export enum PatchFlags {
BAIL = -2,
}
export type VNodeChildAtom = Exclude<VNodeChild, Array<any>>
export type RawSlots = Exclude<
VNodeNormalizedChildren,
Array<any> | null | string
>
export function isFragment(node: VNode): boolean
export function isFragment(node: unknown): node is VNode
export function isFragment(node: unknown): node is VNode {
@ -138,11 +144,18 @@ export const ensureOnlyChild = (children: VNodeArrayChildren | undefined) => {
return children[0]
}
export const flattedChildren = (children: VNodeNormalizedChildren) => {
export type FlattenVNodes = Array<VNodeChildAtom | RawSlots>
export const flattedChildren = (
children: FlattenVNodes | VNode | VNodeNormalizedChildren
): FlattenVNodes => {
const vNodes = isArray(children) ? children : [children]
const result: any[] = []
vNodes.forEach((child: any) => {
if (isArray(child.children)) {
const result: FlattenVNodes = []
vNodes.forEach((child) => {
if (isArray(child)) {
result.push(...flattedChildren(child))
} else if (isVNode(child) && isArray(child.children)) {
result.push(...flattedChildren(child.children))
} else {
result.push(child)