render.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import {
  2. effectScope,
  3. normalizeClass,
  4. normalizeStyle,
  5. toDisplayString
  6. } from 'vue'
  7. import { isArray } from '@vue/shared'
  8. export type Block = Node | Fragment | Block[]
  9. export type Fragment = { nodes: Block; anchor?: Node }
  10. export type BlockFn = (props?: any) => Block
  11. export function render(
  12. comp: BlockFn,
  13. container: string | ParentNode
  14. ): () => void {
  15. const scope = effectScope()
  16. const block = scope.run(() => comp())!
  17. insert(block, (container = normalizeContainer(container)))
  18. return () => {
  19. scope.stop()
  20. remove(block, container as ParentNode)
  21. }
  22. }
  23. export function normalizeContainer(container: string | ParentNode): ParentNode {
  24. return typeof container === 'string'
  25. ? (document.querySelector(container) as ParentNode)
  26. : container
  27. }
  28. export const enum InsertPosition {
  29. FIRST,
  30. LAST
  31. }
  32. export function insert(
  33. block: Block,
  34. parent: ParentNode,
  35. anchor: Node | InsertPosition | null = null
  36. ) {
  37. anchor =
  38. typeof anchor === 'number'
  39. ? anchor === InsertPosition.FIRST
  40. ? parent.firstChild
  41. : null
  42. : anchor
  43. // if (!isHydrating) {
  44. if (block instanceof Node) {
  45. parent.insertBefore(block, anchor)
  46. } else if (isArray(block)) {
  47. for (const child of block) insert(child, parent, anchor)
  48. } else {
  49. insert(block.nodes, parent, anchor)
  50. block.anchor && parent.insertBefore(block.anchor, anchor)
  51. }
  52. // }
  53. }
  54. export function remove(block: Block, parent: ParentNode) {
  55. if (block instanceof Node) {
  56. parent.removeChild(block)
  57. } else if (isArray(block)) {
  58. for (const child of block) remove(child, parent)
  59. } else {
  60. remove(block.nodes, parent)
  61. block.anchor && parent.removeChild(block.anchor)
  62. }
  63. }
  64. export function setText(el: Element, oldVal: any, newVal: any) {
  65. if ((newVal = toDisplayString(newVal)) !== oldVal) {
  66. el.textContent = newVal
  67. }
  68. }
  69. export function setClass(el: Element, oldVal: any, newVal: any) {
  70. if ((newVal = normalizeClass(newVal)) !== oldVal && (newVal || oldVal)) {
  71. el.className = newVal
  72. }
  73. }
  74. export function setStyle(el: HTMLElement, oldVal: any, newVal: any) {
  75. if ((newVal = normalizeStyle(newVal)) !== oldVal && (newVal || oldVal)) {
  76. if (typeof newVal === 'string') {
  77. el.style.cssText = newVal
  78. } else {
  79. // TODO
  80. }
  81. }
  82. }
  83. export function setAttr(el: Element, key: string, oldVal: any, newVal: any) {
  84. if (newVal !== oldVal) {
  85. if (newVal != null) {
  86. el.setAttribute(key, newVal)
  87. } else {
  88. el.removeAttribute(key)
  89. }
  90. }
  91. }
  92. export function setDynamicProp(el: Element, key: string, val: any) {
  93. if (key === 'class') {
  94. setClass(el, void 0, val)
  95. } else if (key === 'style') {
  96. setStyle(el as HTMLElement, void 0, val)
  97. } else if (key in el) {
  98. ;(el as any)[key] = val
  99. } else {
  100. // TODO special checks
  101. setAttr(el, key, void 0, val)
  102. }
  103. }
  104. type Children = Record<number, [ChildNode, Children]>
  105. export function children(n: ChildNode): Children {
  106. return { ...Array.from(n.childNodes).map(n => [n, children(n)]) }
  107. }