vnode.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import { Comment, Fragment, Text, createBlock, createCommentVNode, isVNode, openBlock } from 'vue'
  2. import { camelize, isArray } from '@vue/shared'
  3. import { hasOwn } from '../objects'
  4. import { debugWarn } from '../error'
  5. import type { VNode, VNodeArrayChildren, VNodeChild, VNodeNormalizedChildren } from 'vue'
  6. const SCOPE = 'utils/vue/vnode'
  7. export enum PatchFlags {
  8. TEXT = 1,
  9. CLASS = 2,
  10. STYLE = 4,
  11. PROPS = 8,
  12. FULL_PROPS = 16,
  13. HYDRATE_EVENTS = 32,
  14. STABLE_FRAGMENT = 64,
  15. KEYED_FRAGMENT = 128,
  16. UNKEYED_FRAGMENT = 256,
  17. NEED_PATCH = 512,
  18. DYNAMIC_SLOTS = 1024,
  19. HOISTED = -1,
  20. BAIL = -2,
  21. }
  22. export type VNodeChildAtom = Exclude<VNodeChild, Array<any>>
  23. export type RawSlots = Exclude<VNodeNormalizedChildren, Array<any> | null | string>
  24. export function isFragment(node: VNode): boolean
  25. export function isFragment(node: unknown): node is VNode
  26. export function isFragment(node: unknown): node is VNode {
  27. return isVNode(node) && node.type === Fragment
  28. }
  29. export function isText(node: VNode): boolean
  30. export function isText(node: unknown): node is VNode
  31. export function isText(node: unknown): node is VNode {
  32. return isVNode(node) && node.type === Text
  33. }
  34. export function isComment(node: VNode): boolean
  35. export function isComment(node: unknown): node is VNode
  36. export function isComment(node: unknown): node is VNode {
  37. return isVNode(node) && node.type === Comment
  38. }
  39. const TEMPLATE = 'template'
  40. export function isTemplate(node: VNode): boolean
  41. export function isTemplate(node: unknown): node is VNode
  42. export function isTemplate(node: unknown): node is VNode {
  43. return isVNode(node) && node.type === TEMPLATE
  44. }
  45. /**
  46. * determine if the element is a valid element type rather than fragments and comment e.g. <template> v-if
  47. * @param node {VNode} node to be tested
  48. */
  49. export function isValidElementNode(node: VNode): boolean
  50. export function isValidElementNode(node: unknown): node is VNode
  51. export function isValidElementNode(node: unknown): node is VNode {
  52. return isVNode(node) && !isFragment(node) && !isComment(node)
  53. }
  54. /**
  55. * get a valid child node (not fragment nor comment)
  56. * @param node {VNode} node to be searched
  57. * @param depth {number} depth to be searched
  58. */
  59. function getChildren(node: VNodeNormalizedChildren | VNodeChild, depth: number): VNodeNormalizedChildren | VNodeChild {
  60. if (isComment(node)) return
  61. if (isFragment(node) || isTemplate(node)) {
  62. return depth > 0 ? getFirstValidNode(node.children, depth - 1) : undefined
  63. }
  64. return node
  65. }
  66. export const getFirstValidNode = (nodes: VNodeNormalizedChildren, maxDepth = 3) => {
  67. if (Array.isArray(nodes)) {
  68. return getChildren(nodes[0], maxDepth)
  69. } else {
  70. return getChildren(nodes, maxDepth)
  71. }
  72. }
  73. export function renderIf(condition: boolean, ...args: Parameters<typeof createBlock>) {
  74. return condition ? renderBlock(...args) : createCommentVNode('v-if', true)
  75. }
  76. export function renderBlock(...args: Parameters<typeof createBlock>) {
  77. return openBlock(), createBlock(...args)
  78. }
  79. export const getNormalizedProps = (node: VNode) => {
  80. if (!isVNode(node)) {
  81. debugWarn(SCOPE, '[getNormalizedProps] must be a VNode')
  82. return {}
  83. }
  84. const raw = node.props || {}
  85. const type = (isVNode(node.type) ? node.type.props : undefined) || {}
  86. const props: Record<string, any> = {}
  87. Object.keys(type).forEach(key => {
  88. if (hasOwn(type[key], 'default')) {
  89. props[key] = type[key].default
  90. }
  91. })
  92. Object.keys(raw).forEach(key => {
  93. props[camelize(key)] = raw[key]
  94. })
  95. return props
  96. }
  97. export const ensureOnlyChild = (children: VNodeArrayChildren | undefined) => {
  98. if (!isArray(children) || children.length > 1) {
  99. throw new Error('expect to receive a single Vue element child')
  100. }
  101. return children[0]
  102. }
  103. export type FlattenVNodes = Array<VNodeChildAtom | RawSlots>
  104. export const flattedChildren = (children: FlattenVNodes | VNode | VNodeNormalizedChildren): FlattenVNodes => {
  105. const vNodes = isArray(children) ? children : [children]
  106. const result: FlattenVNodes = []
  107. vNodes.forEach(child => {
  108. if (isArray(child)) {
  109. result.push(...flattedChildren(child))
  110. } else if (isVNode(child) && isArray(child.children)) {
  111. result.push(...flattedChildren(child.children))
  112. } else {
  113. result.push(child)
  114. }
  115. })
  116. return result
  117. }