vnode.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  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(
  60. node: VNodeNormalizedChildren | VNodeChild,
  61. depth: number,
  62. ): VNodeNormalizedChildren | VNodeChild {
  63. if (isComment(node)) return;
  64. if (isFragment(node) || isTemplate(node)) {
  65. return depth > 0 ? getFirstValidNode(node.children, depth - 1) : undefined;
  66. }
  67. return node;
  68. }
  69. export const getFirstValidNode = (nodes: VNodeNormalizedChildren, maxDepth = 3) => {
  70. if (Array.isArray(nodes)) {
  71. return getChildren(nodes[0], maxDepth);
  72. } else {
  73. return getChildren(nodes, maxDepth);
  74. }
  75. };
  76. export function renderIf(condition: boolean, ...args: Parameters<typeof createBlock>) {
  77. return condition ? renderBlock(...args) : createCommentVNode('v-if', true);
  78. }
  79. export function renderBlock(...args: Parameters<typeof createBlock>) {
  80. return openBlock(), createBlock(...args);
  81. }
  82. export const getNormalizedProps = (node: VNode) => {
  83. if (!isVNode(node)) {
  84. debugWarn(SCOPE, '[getNormalizedProps] must be a VNode');
  85. return {};
  86. }
  87. const raw = node.props || {};
  88. const type = (isVNode(node.type) ? node.type.props : undefined) || {};
  89. const props: Record<string, any> = {};
  90. Object.keys(type).forEach((key) => {
  91. if (hasOwn(type[key], 'default')) {
  92. props[key] = type[key].default;
  93. }
  94. });
  95. Object.keys(raw).forEach((key) => {
  96. props[camelize(key)] = raw[key];
  97. });
  98. return props;
  99. };
  100. export const ensureOnlyChild = (children: VNodeArrayChildren | undefined) => {
  101. if (!isArray(children) || children.length > 1) {
  102. throw new Error('expect to receive a single Vue element child');
  103. }
  104. return children[0];
  105. };
  106. export type FlattenVNodes = Array<VNodeChildAtom | RawSlots>;
  107. export const flattedChildren = (
  108. children: FlattenVNodes | VNode | VNodeNormalizedChildren,
  109. ): FlattenVNodes => {
  110. const vNodes = isArray(children) ? children : [children];
  111. const result: FlattenVNodes = [];
  112. vNodes.forEach((child) => {
  113. if (isArray(child)) {
  114. result.push(...flattedChildren(child));
  115. } else if (isVNode(child) && isArray(child.children)) {
  116. result.push(...flattedChildren(child.children));
  117. } else {
  118. result.push(child);
  119. }
  120. });
  121. return result;
  122. };