Vue 3中如何判断指定组件实例是否为另一组件的后代
Vue 3 判断组件后代关系实现方案
问题背景
Vue 2 中可通过递归遍历组件实例的$children属性,判断指定uid的组件是否在某组件的后代树中,但Vue 3 移除了$children属性,直接遍历componentA.$.subTree.children的方案会被Transition、KeepAlive等内置抽象组件阻断,常规的ref、slot方案又需要侵入业务组件、提前感知模板结构,无法适配任意组件出发的全树遍历场景。
可行实现
方案1:vnode树深度遍历(仅需目标组件uid)
该方案会递归遍历组件渲染出的完整vnode树,自动识别组件类型节点、兼容所有内置抽象组件,不需要侵入业务代码。
/** * 校验指定uid的组件是否为根组件的后代节点 * @param rootComponent 遍历起始的组件实例 * @param targetUid 目标组件的唯一uid * @returns 存在后代关系返回true,否则返回false */ function isDescendantComponent(rootComponent: any, targetUid: string | number): boolean { // 先匹配根组件自身 if (String(rootComponent.$.uid) === String(targetUid)) return true const traverseVNode = (vnode: any): boolean => { if (!vnode) return false // 处理多节点数组(多根节点、Fragment返回值场景) if (Array.isArray(vnode)) { return vnode.some(child => traverseVNode(child)) } // 遇到组件类型vnode:先匹配uid,再递归组件的渲染子树 if (vnode.component) { if (String(vnode.component.uid) === String(targetUid)) return true // 递归组件subTree,自动覆盖Transition/KeepAlive/Teleport/Suspense等内置组件场景 if (traverseVNode(vnode.component.subTree)) return true } // 处理普通DOM节点、文本节点的子节点 if (vnode.children && typeof vnode.children === 'object') { return Object.values(vnode.children).some(child => traverseVNode(child)) } return false } return traverseVNode(rootComponent.$.subTree) }
注意:该方案不需要修改任何业务组件,从任意组件实例出发都能遍历到其渲染出的所有下属组件,不会被抽象组件阻断遍历路径。
方案2:父链反向查找(可直接拿到目标组件实例时性能最优)
如果场景中可以直接获取到待判断的组件B实例,不需要靠uid搜索,完全不用遍历整棵子树,顺着Vue内部维护的父组件链向上查找即可,性能远高于正向遍历,且天然兼容所有内置组件。
/** * 通过父组件链反向判断后代关系 * @param componentA 祖先组件实例 * @param componentB 待校验的后代组件实例 * @returns 存在后代关系返回true,否则返回false */ function isDescendantByParentChain(componentA: any, componentB: any): boolean { let currentNode = componentB.$ const ancestorUid = String(componentA.$.uid) while (currentNode) { if (String(currentNode.uid) === ancestorUid) return true currentNode = currentNode.parent } return false }
选型建议
- 仅持有目标组件uid、无法直接拿到目标组件实例时,选方案1
- 可直接获取到待判断的两个组件实例时,优先选方案2,逻辑更简单、性能更高
内容的提问来源于stack exchange,提问作者Quan
相关产品推荐
相关产品推荐

