hmr.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import {
  2. isKeepAlive,
  3. popWarningContext,
  4. pushWarningContext,
  5. setCurrentInstance,
  6. } from '@vue/runtime-dom'
  7. import { type Block, insert, normalizeBlock, remove } from './block'
  8. import {
  9. type VaporComponent,
  10. type VaporComponentInstance,
  11. createComponent,
  12. devRender,
  13. isVaporComponent,
  14. mountComponent,
  15. unmountComponent,
  16. } from './component'
  17. import { isArray } from '@vue/shared'
  18. import { isFragment } from './fragment'
  19. export function hmrRerender(instance: VaporComponentInstance): void {
  20. const normalized = normalizeBlock(instance.block)
  21. const parent = normalized[0].parentNode!
  22. const anchor = normalized[normalized.length - 1].nextSibling
  23. // reset scope to avoid stale effects
  24. instance.scope.reset()
  25. remove(instance.block, parent)
  26. const prev = setCurrentInstance(instance)
  27. pushWarningContext(instance)
  28. devRender(instance)
  29. popWarningContext()
  30. setCurrentInstance(...prev)
  31. insert(instance.block, parent, anchor)
  32. }
  33. export function hmrReload(
  34. instance: VaporComponentInstance,
  35. newComp: VaporComponent,
  36. ): void {
  37. // If parent is KeepAlive, rerender it so new component goes through
  38. // KeepAlive's slot rendering flow to receive activated hooks properly
  39. if (instance.parent && isKeepAlive(instance.parent)) {
  40. instance.parent.hmrRerender!()
  41. return
  42. }
  43. const normalized = normalizeBlock(instance.block)
  44. const parent = normalized[0].parentNode!
  45. const anchor = normalized[normalized.length - 1].nextSibling
  46. unmountComponent(instance, parent)
  47. const parentInstance = instance.parent as VaporComponentInstance | null
  48. const prev = setCurrentInstance(parentInstance)
  49. const newInstance = createComponent(
  50. newComp,
  51. instance.rawProps,
  52. instance.rawSlots,
  53. instance.isSingleRoot,
  54. undefined,
  55. instance.appContext,
  56. )
  57. setCurrentInstance(...prev)
  58. mountComponent(newInstance, parent, anchor)
  59. updateParentBlockOnHmrReload(parentInstance, instance, newInstance)
  60. updateParentTeleportOnHmrReload(instance, newInstance)
  61. }
  62. /**
  63. * dev only
  64. * update parentInstance.block to ensure that the correct parent and
  65. * anchor are found during parentInstance HMR rerender/reload, as
  66. * `normalizeBlock` relies on the current instance.block
  67. */
  68. function updateParentBlockOnHmrReload(
  69. parentInstance: VaporComponentInstance | null,
  70. instance: VaporComponentInstance,
  71. newInstance: VaporComponentInstance,
  72. ): void {
  73. if (parentInstance) {
  74. parentInstance.block = replaceBlockInstance(
  75. parentInstance.block,
  76. instance,
  77. newInstance,
  78. )
  79. }
  80. }
  81. /**
  82. * dev only
  83. * during root component HMR reload, since the old component will be unmounted
  84. * and a new one will be mounted, we need to update the teleport's nodes
  85. * to ensure that the correct parent and anchor are found during parentInstance
  86. * HMR rerender/reload, as `normalizeBlock` relies on the current instance.block
  87. */
  88. export function updateParentTeleportOnHmrReload(
  89. instance: VaporComponentInstance,
  90. newInstance: VaporComponentInstance,
  91. ): void {
  92. const teleport = instance.parentTeleport
  93. if (teleport) {
  94. newInstance.parentTeleport = teleport
  95. teleport.nodes = replaceBlockInstance(teleport.nodes, instance, newInstance)
  96. }
  97. }
  98. function replaceBlockInstance(
  99. block: Block,
  100. instance: VaporComponentInstance,
  101. newInstance: VaporComponentInstance,
  102. ): Block {
  103. if (block === instance) return newInstance
  104. if (isArray(block)) {
  105. for (let i = 0; i < block.length; i++) {
  106. block[i] = replaceBlockInstance(block[i], instance, newInstance)
  107. }
  108. return block
  109. }
  110. if (isVaporComponent(block)) {
  111. block.block = replaceBlockInstance(block.block, instance, newInstance)
  112. return block
  113. }
  114. if (isFragment(block)) {
  115. block.nodes = replaceBlockInstance(block.nodes, instance, newInstance)
  116. return block
  117. }
  118. return block
  119. }