]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(runtime-vapor): render component
author三咲智子 Kevin Deng <sxzz@sxzz.moe>
Wed, 6 Dec 2023 06:59:11 +0000 (14:59 +0800)
committer三咲智子 Kevin Deng <sxzz@sxzz.moe>
Wed, 6 Dec 2023 10:49:41 +0000 (18:49 +0800)
packages/runtime-vapor/src/component.ts
packages/runtime-vapor/src/dom.ts [new file with mode: 0644]
packages/runtime-vapor/src/index.ts
packages/runtime-vapor/src/render.ts
playground/src/main.ts

index ead75c932890e971c5d78bd63dbdcce73156eb91..0ea657d24e5d06630ad6fd216568fabee0a9bb9d 100644 (file)
@@ -1,14 +1,23 @@
 import { EffectScope } from '@vue/reactivity'
-import { Block, BlockFn } from './render'
+import { Block } from './render'
 import { DirectiveBinding } from './directives'
 
+export type SetupFn = (props: any, ctx: any) => Block | Data
+export type FunctionalComponent = SetupFn & {
+  render(ctx: any): Block
+}
+export interface ObjectComponent {
+  setup: SetupFn
+  render(ctx: any): Block
+}
+
 export interface ComponentInternalInstance {
   uid: number
   container: ParentNode
   block: Block | null
   scope: EffectScope
 
-  component: BlockFn
+  component: FunctionalComponent | ObjectComponent
   isMounted: boolean
 
   /** directives */
@@ -32,7 +41,7 @@ export const unsetCurrentInstance = () => {
 
 let uid = 0
 export const createComponentInstance = (
-  component: BlockFn,
+  component: ObjectComponent | FunctionalComponent,
 ): ComponentInternalInstance => {
   const instance: ComponentInternalInstance = {
     uid: uid++,
diff --git a/packages/runtime-vapor/src/dom.ts b/packages/runtime-vapor/src/dom.ts
new file mode 100644 (file)
index 0000000..92bc1e2
--- /dev/null
@@ -0,0 +1,113 @@
+import {
+  isArray,
+  normalizeClass,
+  normalizeStyle,
+  toDisplayString,
+} from '@vue/shared'
+import type { Block, ParentBlock } from './render'
+
+export function insert(
+  block: Block,
+  parent: ParentNode,
+  anchor: Node | null = null,
+) {
+  // if (!isHydrating) {
+  if (block instanceof Node) {
+    parent.insertBefore(block, anchor)
+  } else if (isArray(block)) {
+    for (const child of block) insert(child, parent, anchor)
+  } else {
+    insert(block.nodes, parent, anchor)
+    parent.insertBefore(block.anchor, anchor)
+  }
+  // }
+}
+
+export function prepend(parent: ParentBlock, ...nodes: Node[]) {
+  if (parent instanceof Node) {
+    // TODO use insertBefore for better performance https://jsbench.me/rolpg250hh/1
+    parent.prepend(...nodes)
+  } else if (isArray(parent)) {
+    parent.unshift(...nodes)
+  }
+}
+
+export function append(parent: ParentBlock, ...nodes: Node[]) {
+  if (parent instanceof Node) {
+    // TODO use insertBefore for better performance
+    parent.append(...nodes)
+  } else if (isArray(parent)) {
+    parent.push(...nodes)
+  }
+}
+
+export function remove(block: Block, parent: ParentNode) {
+  if (block instanceof Node) {
+    parent.removeChild(block)
+  } else if (isArray(block)) {
+    for (const child of block) remove(child, parent)
+  } else {
+    remove(block.nodes, parent)
+    block.anchor && parent.removeChild(block.anchor)
+  }
+}
+
+export function setText(el: Element, oldVal: any, newVal: any) {
+  if ((newVal = toDisplayString(newVal)) !== oldVal) {
+    el.textContent = newVal
+  }
+}
+
+export function setHtml(el: Element, oldVal: any, newVal: any) {
+  if (newVal !== oldVal) {
+    el.innerHTML = newVal
+  }
+}
+
+export function setClass(el: Element, oldVal: any, newVal: any) {
+  if ((newVal = normalizeClass(newVal)) !== oldVal && (newVal || oldVal)) {
+    el.className = newVal
+  }
+}
+
+export function setStyle(el: HTMLElement, oldVal: any, newVal: any) {
+  if ((newVal = normalizeStyle(newVal)) !== oldVal && (newVal || oldVal)) {
+    if (typeof newVal === 'string') {
+      el.style.cssText = newVal
+    } else {
+      // TODO
+    }
+  }
+}
+
+export function setAttr(el: Element, key: string, oldVal: any, newVal: any) {
+  if (newVal !== oldVal) {
+    if (newVal != null) {
+      el.setAttribute(key, newVal)
+    } else {
+      el.removeAttribute(key)
+    }
+  }
+}
+
+export function setDynamicProp(el: Element, key: string, val: any) {
+  if (key === 'class') {
+    setClass(el, void 0, val)
+  } else if (key === 'style') {
+    setStyle(el as HTMLElement, void 0, val)
+  } else if (key in el) {
+    ;(el as any)[key] = val
+  } else {
+    // TODO special checks
+    setAttr(el, key, void 0, val)
+  }
+}
+
+type Children = Record<number, [ChildNode, Children]>
+export function children(n: ChildNode): Children {
+  return { ...Array.from(n.childNodes).map((n) => [n, children(n)]) }
+}
+
+export function createTextNode(val: unknown): Text {
+  return document.createTextNode(toDisplayString(val))
+}
index 5533dfc826c81d405b37a4ad8dae9d67e6b788a5..63985f7ec0af0175512691bb5be5d15292b40778 100644 (file)
@@ -42,3 +42,4 @@ export * from './render'
 export * from './template'
 export * from './scheduler'
 export * from './directives'
+export * from './dom'
index 64d8f0a3d77cb635aab93d24de3b5bb715c917af..74e88eaceb470e89973d3cd37769685d4befaf9b 100644 (file)
@@ -1,24 +1,22 @@
+import { reactive } from '@vue/reactivity'
 import {
-  isArray,
-  normalizeClass,
-  normalizeStyle,
-  toDisplayString,
-} from '@vue/shared'
-import {
-  ComponentInternalInstance,
+  type ComponentInternalInstance,
+  type FunctionalComponent,
+  type ObjectComponent,
   createComponentInstance,
   setCurrentInstance,
   unsetCurrentInstance,
 } from './component'
 import { invokeDirectiveHook } from './directives'
+import { insert, remove } from './dom'
 
 export type Block = Node | Fragment | Block[]
 export type ParentBlock = ParentNode | Node[]
 export type Fragment = { nodes: Block; anchor: Node }
-export type BlockFn = (props?: any) => Block
+export type BlockFn = (props: any, ctx: any) => Block
 
 export function render(
-  comp: BlockFn,
+  comp: ObjectComponent | FunctionalComponent,
   container: string | ParentNode,
 ): ComponentInternalInstance {
   const instance = createComponentInstance(comp)
@@ -33,16 +31,28 @@ export function normalizeContainer(container: string | ParentNode): ParentNode {
     : container
 }
 
-export const mountComponent = (
+export function mountComponent(
   instance: ComponentInternalInstance,
   container: ParentNode,
-) => {
+) {
   instance.container = container
 
   setCurrentInstance(instance)
-  const block = instance.scope.run(
-    () => (instance.block = instance.component()),
-  )!
+  const block = instance.scope.run(() => {
+    const { component } = instance
+    const props = {}
+    const ctx = { expose: () => {} }
+
+    const setupFn =
+      typeof component === 'function' ? component : component.setup
+
+    const state = setupFn(props, ctx)
+    if (state && '__isScriptSetup' in state) {
+      return (instance.block = component.render(reactive(state)))
+    } else {
+      return (instance.block = state as Block)
+    }
+  })!
 
   invokeDirectiveHook(instance, 'beforeMount')
   insert(block, instance.container)
@@ -54,7 +64,7 @@ export const mountComponent = (
   // m && invoke(m)
 }
 
-export const unmountComponent = (instance: ComponentInternalInstance) => {
+export function unmountComponent(instance: ComponentInternalInstance) {
   const { container, block, scope } = instance
 
   invokeDirectiveHook(instance, 'beforeUnmount')
@@ -68,109 +78,3 @@ export const unmountComponent = (instance: ComponentInternalInstance) => {
   // const { um } = instance
   // um && invoke(um)
 }
-
-export function insert(
-  block: Block,
-  parent: ParentNode,
-  anchor: Node | null = null,
-) {
-  // if (!isHydrating) {
-  if (block instanceof Node) {
-    parent.insertBefore(block, anchor)
-  } else if (isArray(block)) {
-    for (const child of block) insert(child, parent, anchor)
-  } else {
-    insert(block.nodes, parent, anchor)
-    parent.insertBefore(block.anchor, anchor)
-  }
-  // }
-}
-
-export function prepend(parent: ParentBlock, ...nodes: Node[]) {
-  if (parent instanceof Node) {
-    // TODO use insertBefore for better performance https://jsbench.me/rolpg250hh/1
-    parent.prepend(...nodes)
-  } else if (isArray(parent)) {
-    parent.unshift(...nodes)
-  }
-}
-
-export function append(parent: ParentBlock, ...nodes: Node[]) {
-  if (parent instanceof Node) {
-    // TODO use insertBefore for better performance
-    parent.append(...nodes)
-  } else if (isArray(parent)) {
-    parent.push(...nodes)
-  }
-}
-
-export function remove(block: Block, parent: ParentNode) {
-  if (block instanceof Node) {
-    parent.removeChild(block)
-  } else if (isArray(block)) {
-    for (const child of block) remove(child, parent)
-  } else {
-    remove(block.nodes, parent)
-    block.anchor && parent.removeChild(block.anchor)
-  }
-}
-
-export function setText(el: Element, oldVal: any, newVal: any) {
-  if ((newVal = toDisplayString(newVal)) !== oldVal) {
-    el.textContent = newVal
-  }
-}
-
-export function setHtml(el: Element, oldVal: any, newVal: any) {
-  if (newVal !== oldVal) {
-    el.innerHTML = newVal
-  }
-}
-
-export function setClass(el: Element, oldVal: any, newVal: any) {
-  if ((newVal = normalizeClass(newVal)) !== oldVal && (newVal || oldVal)) {
-    el.className = newVal
-  }
-}
-
-export function setStyle(el: HTMLElement, oldVal: any, newVal: any) {
-  if ((newVal = normalizeStyle(newVal)) !== oldVal && (newVal || oldVal)) {
-    if (typeof newVal === 'string') {
-      el.style.cssText = newVal
-    } else {
-      // TODO
-    }
-  }
-}
-
-export function setAttr(el: Element, key: string, oldVal: any, newVal: any) {
-  if (newVal !== oldVal) {
-    if (newVal != null) {
-      el.setAttribute(key, newVal)
-    } else {
-      el.removeAttribute(key)
-    }
-  }
-}
-
-export function setDynamicProp(el: Element, key: string, val: any) {
-  if (key === 'class') {
-    setClass(el, void 0, val)
-  } else if (key === 'style') {
-    setStyle(el as HTMLElement, void 0, val)
-  } else if (key in el) {
-    ;(el as any)[key] = val
-  } else {
-    // TODO special checks
-    setAttr(el, key, void 0, val)
-  }
-}
-
-type Children = Record<number, [ChildNode, Children]>
-export function children(n: ChildNode): Children {
-  return { ...Array.from(n.childNodes).map((n) => [n, children(n)]) }
-}
-
-export function createTextNode(val: unknown): Text {
-  return document.createTextNode(toDisplayString(val))
-}
index 06b2c60ad079d4c99ef54c904ed77c1db69ce930..43565bc94c6584965054bfa68f60340091fdc2df 100644 (file)
@@ -3,9 +3,4 @@ import { render } from 'vue/vapor'
 const modules = import.meta.glob<any>('./*.vue')
 const mod = (modules['.' + location.pathname] || modules['./App.vue'])()
 
-mod.then(({ default: m }) => {
-  render(() => {
-    const returned = m.setup?.({}, { expose() {} })
-    return m.render(returned)
-  }, '#app')
-})
+mod.then(({ default: mod }) => render(mod, '#app'))