Skip to content

Commit

Permalink
perf(components): [tabs] improve order performance
Browse files Browse the repository at this point in the history
  • Loading branch information
sxzz committed Sep 16, 2022
1 parent 116c1da commit ab19e8f
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 40 deletions.
6 changes: 3 additions & 3 deletions packages/components/menu/src/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -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
Expand Down
44 changes: 11 additions & 33 deletions packages/components/tabs/src/tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@ import {
getCurrentInstance,
nextTick,
provide,
reactive,
ref,
renderSlot,
shallowReactive,
shallowRef,
watch,
} from 'vue'
import {
buildProps,
definePropType,
flattedChildren,
getFirstValidNode,
isNumber,
isString,
isUndefined,
Expand All @@ -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
Expand Down Expand Up @@ -79,6 +79,8 @@ export const tabsEmits = {
}
export type TabsEmits = typeof tabsEmits

export type TabsPanes = Record<number, TabsPaneContext>

export default defineComponent({
name: 'ElTabs',

Expand All @@ -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'
)
Expand Down Expand Up @@ -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, {
Expand Down
20 changes: 20 additions & 0 deletions packages/components/tabs/src/utils/pane.ts
Original file line number Diff line number Diff line change
@@ -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)
}
21 changes: 17 additions & 4 deletions packages/utils/vue/vnode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit ab19e8f

Please sign in to comment.