useChildren.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import {
  2. provide,
  3. reactive,
  4. getCurrentInstance,
  5. type VNode,
  6. type InjectionKey,
  7. type VNodeNormalizedChildren,
  8. type ComponentPublicInstance,
  9. type ComponentInternalInstance
  10. } from 'vue'
  11. // 小程序端不支持从vue导出的isVNode方法,参考uni-mp-vue的实现
  12. function isVNode(value: any): value is VNode {
  13. return value ? value.__v_isVNode === true : false
  14. }
  15. export function flattenVNodes(children: VNodeNormalizedChildren) {
  16. const result: VNode[] = []
  17. const traverse = (children: VNodeNormalizedChildren) => {
  18. if (Array.isArray(children)) {
  19. children.forEach((child) => {
  20. if (isVNode(child)) {
  21. result.push(child)
  22. if (child.component?.subTree) {
  23. result.push(child.component.subTree)
  24. traverse(child.component.subTree.children)
  25. }
  26. if (child.children) {
  27. traverse(child.children)
  28. }
  29. }
  30. })
  31. }
  32. }
  33. traverse(children)
  34. return result
  35. }
  36. const findVNodeIndex = (vnodes: VNode[], vnode: VNode) => {
  37. const index = vnodes.indexOf(vnode)
  38. if (index === -1) {
  39. return vnodes.findIndex((item) => vnode.key !== undefined && vnode.key !== null && item.type === vnode.type && item.key === vnode.key)
  40. }
  41. return index
  42. }
  43. // sort children instances by vnodes order
  44. export function sortChildren(
  45. parent: ComponentInternalInstance,
  46. publicChildren: ComponentPublicInstance[],
  47. internalChildren: ComponentInternalInstance[]
  48. ) {
  49. const vnodes = parent && parent.subTree && parent.subTree.children ? flattenVNodes(parent.subTree.children) : []
  50. internalChildren.sort((a, b) => findVNodeIndex(vnodes, a.vnode) - findVNodeIndex(vnodes, b.vnode))
  51. const orderedPublicChildren = internalChildren.map((item) => item.proxy!)
  52. publicChildren.sort((a, b) => {
  53. const indexA = orderedPublicChildren.indexOf(a)
  54. const indexB = orderedPublicChildren.indexOf(b)
  55. return indexA - indexB
  56. })
  57. }
  58. export function useChildren<
  59. // eslint-disable-next-line
  60. Child extends ComponentPublicInstance = ComponentPublicInstance<{}, any>,
  61. ProvideValue = never
  62. >(key: InjectionKey<ProvideValue>) {
  63. const publicChildren: Child[] = reactive([])
  64. const internalChildren: ComponentInternalInstance[] = reactive([])
  65. const parent = getCurrentInstance()!
  66. const linkChildren = (value?: ProvideValue) => {
  67. const link = (child: ComponentInternalInstance) => {
  68. if (child.proxy) {
  69. internalChildren.push(child)
  70. publicChildren.push(child.proxy as Child)
  71. sortChildren(parent, publicChildren, internalChildren)
  72. }
  73. }
  74. const unlink = (child: ComponentInternalInstance) => {
  75. const index = internalChildren.indexOf(child)
  76. publicChildren.splice(index, 1)
  77. internalChildren.splice(index, 1)
  78. }
  79. provide(
  80. key,
  81. Object.assign(
  82. {
  83. link,
  84. unlink,
  85. children: publicChildren,
  86. internalChildren
  87. },
  88. value
  89. )
  90. )
  91. }
  92. return {
  93. children: publicChildren,
  94. linkChildren
  95. }
  96. }