componentRenderUtils.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import {
  2. ComponentInternalInstance,
  3. FunctionalComponent,
  4. Data
  5. } from './component'
  6. import { VNode, normalizeVNode, createVNode, Comment } from './vnode'
  7. import { ShapeFlags } from './shapeFlags'
  8. import { handleError, ErrorCodes } from './errorHandling'
  9. import { PatchFlags } from '@vue/shared'
  10. // mark the current rendering instance for asset resolution (e.g.
  11. // resolveComponent, resolveDirective) during render
  12. export let currentRenderingInstance: ComponentInternalInstance | null = null
  13. export function renderComponentRoot(
  14. instance: ComponentInternalInstance
  15. ): VNode {
  16. const {
  17. type: Component,
  18. vnode,
  19. renderProxy,
  20. props,
  21. slots,
  22. attrs,
  23. emit
  24. } = instance
  25. let result
  26. currentRenderingInstance = instance
  27. try {
  28. if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
  29. result = normalizeVNode(instance.render!.call(renderProxy))
  30. } else {
  31. // functional
  32. const render = Component as FunctionalComponent
  33. result = normalizeVNode(
  34. render.length > 1
  35. ? render(props, {
  36. attrs,
  37. slots,
  38. emit
  39. })
  40. : render(props, null as any)
  41. )
  42. }
  43. } catch (err) {
  44. handleError(err, instance, ErrorCodes.RENDER_FUNCTION)
  45. result = createVNode(Comment)
  46. }
  47. currentRenderingInstance = null
  48. return result
  49. }
  50. export function shouldUpdateComponent(
  51. prevVNode: VNode,
  52. nextVNode: VNode,
  53. optimized?: boolean
  54. ): boolean {
  55. const { props: prevProps, children: prevChildren } = prevVNode
  56. const { props: nextProps, children: nextChildren, patchFlag } = nextVNode
  57. if (patchFlag > 0) {
  58. if (patchFlag & PatchFlags.DYNAMIC_SLOTS) {
  59. // slot content that references values that might have changed,
  60. // e.g. in a v-for
  61. return true
  62. }
  63. if (patchFlag & PatchFlags.FULL_PROPS) {
  64. // presence of this flag indicates props are always non-null
  65. return hasPropsChanged(prevProps!, nextProps!)
  66. } else if (patchFlag & PatchFlags.PROPS) {
  67. const dynamicProps = nextVNode.dynamicProps!
  68. for (let i = 0; i < dynamicProps.length; i++) {
  69. const key = dynamicProps[i]
  70. if (nextProps![key] !== prevProps![key]) {
  71. return true
  72. }
  73. }
  74. }
  75. } else if (!optimized) {
  76. // this path is only taken by manually written render functions
  77. // so presence of any children leads to a forced update
  78. if (prevChildren != null || nextChildren != null) {
  79. return true
  80. }
  81. if (prevProps === nextProps) {
  82. return false
  83. }
  84. if (prevProps === null) {
  85. return nextProps !== null
  86. }
  87. if (nextProps === null) {
  88. return prevProps !== null
  89. }
  90. return hasPropsChanged(prevProps, nextProps)
  91. }
  92. return false
  93. }
  94. function hasPropsChanged(prevProps: Data, nextProps: Data): boolean {
  95. const nextKeys = Object.keys(nextProps)
  96. if (nextKeys.length !== Object.keys(prevProps).length) {
  97. return true
  98. }
  99. for (let i = 0; i < nextKeys.length; i++) {
  100. const key = nextKeys[i]
  101. if (nextProps[key] !== prevProps[key]) {
  102. return true
  103. }
  104. }
  105. return false
  106. }