render.ts 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import { markRaw, proxyRefs } from '@vue/reactivity'
  2. import { type Data } from '@vue/shared'
  3. import {
  4. type Component,
  5. type ComponentInternalInstance,
  6. createComponentInstance,
  7. setCurrentInstance,
  8. unsetCurrentInstance,
  9. } from './component'
  10. import { initProps } from './componentProps'
  11. import { invokeDirectiveHook } from './directive'
  12. import { insert, remove } from './dom'
  13. import { PublicInstanceProxyHandlers } from './componentPublicInstance'
  14. export type Block = Node | Fragment | Block[]
  15. export type ParentBlock = ParentNode | Node[]
  16. export type Fragment = { nodes: Block; anchor: Node }
  17. export type BlockFn = (props: any, ctx: any) => Block
  18. export function render(
  19. comp: Component,
  20. props: Data,
  21. container: string | ParentNode,
  22. ): ComponentInternalInstance {
  23. const instance = createComponentInstance(comp)
  24. initProps(instance, props)
  25. return mountComponent(instance, (container = normalizeContainer(container)))
  26. }
  27. export function normalizeContainer(container: string | ParentNode): ParentNode {
  28. return typeof container === 'string'
  29. ? (document.querySelector(container) as ParentNode)
  30. : container
  31. }
  32. export function mountComponent(
  33. instance: ComponentInternalInstance,
  34. container: ParentNode,
  35. ) {
  36. instance.container = container
  37. setCurrentInstance(instance)
  38. const block = instance.scope.run(() => {
  39. const { component, props } = instance
  40. const ctx = { expose: () => {} }
  41. const setupFn =
  42. typeof component === 'function' ? component : component.setup
  43. instance.proxy = markRaw(
  44. new Proxy({ _: instance }, PublicInstanceProxyHandlers),
  45. )
  46. const state = setupFn && setupFn(props, ctx)
  47. let block: Block | null = null
  48. if (state && '__isScriptSetup' in state) {
  49. instance.setupState = proxyRefs(state)
  50. block = component.render(instance.proxy)
  51. } else {
  52. block = state as Block
  53. }
  54. if (block instanceof DocumentFragment) block = Array.from(block.childNodes)
  55. return (instance.block = block)
  56. })!
  57. invokeDirectiveHook(instance, 'beforeMount')
  58. insert(block, instance.container)
  59. instance.isMountedRef.value = true
  60. invokeDirectiveHook(instance, 'mounted')
  61. unsetCurrentInstance()
  62. // TODO: lifecycle hooks (mounted, ...)
  63. // const { m } = instance
  64. // m && invoke(m)
  65. return instance
  66. }
  67. export function unmountComponent(instance: ComponentInternalInstance) {
  68. const { container, block, scope } = instance
  69. invokeDirectiveHook(instance, 'beforeUnmount')
  70. scope.stop()
  71. block && remove(block, container)
  72. instance.isMountedRef.value = false
  73. invokeDirectiveHook(instance, 'unmounted')
  74. unsetCurrentInstance()
  75. // TODO: lifecycle hooks (unmounted, ...)
  76. // const { um } = instance
  77. // um && invoke(um)
  78. }