]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: save
authorEvan You <evan@vuejs.org>
Mon, 2 Dec 2024 01:36:49 +0000 (09:36 +0800)
committerEvan You <evan@vuejs.org>
Mon, 2 Dec 2024 01:36:49 +0000 (09:36 +0800)
packages/runtime-vapor/src/apiCreateComponentSimple.ts [new file with mode: 0644]
packages/runtime-vapor/src/apiRender.ts
packages/runtime-vapor/src/component.ts
packages/runtime-vapor/src/dom/element.ts
packages/runtime-vapor/src/index.ts
playground/src/App.vue
playground/src/main.ts
playground/vite.prod.config.ts

diff --git a/packages/runtime-vapor/src/apiCreateComponentSimple.ts b/packages/runtime-vapor/src/apiCreateComponentSimple.ts
new file mode 100644 (file)
index 0000000..04ca9b0
--- /dev/null
@@ -0,0 +1,69 @@
+import {
+  EffectScope,
+  ReactiveEffect,
+  pauseTracking,
+  proxyRefs,
+  resetTracking,
+} from '@vue/reactivity'
+import {
+  type Component,
+  type ComponentInternalInstance,
+  createSetupContext,
+} from './component'
+import { EMPTY_OBJ, isFunction } from '@vue/shared'
+import { type SchedulerJob, queueJob } from '../../runtime-core/src/scheduler'
+
+export function createComponentSimple(component: any, rawProps?: any): any {
+  const instance = new ComponentInstance(
+    component,
+    rawProps,
+  ) as any as ComponentInternalInstance
+  pauseTracking()
+  let prevInstance = currentInstance
+  currentInstance = instance
+  instance.scope.on()
+  const setupFn = isFunction(component) ? component : component.setup
+  const setupContext = setupFn.length > 1 ? createSetupContext(instance) : null
+  const node = setupFn(
+    // TODO __DEV__ ? shallowReadonly(props) :
+    instance.props,
+    setupContext,
+  )
+  instance.scope.off()
+  currentInstance = prevInstance
+  resetTracking()
+  node.__vue__ = instance
+  return node
+}
+
+let uid = 0
+let currentInstance: ComponentInstance | null = null
+
+export class ComponentInstance {
+  type: any
+  uid: number = uid++
+  scope: EffectScope = new EffectScope(true)
+  props: any
+  constructor(comp: Component, rawProps: any) {
+    this.type = comp
+    // init props
+    this.props = rawProps ? proxyRefs(rawProps) : EMPTY_OBJ
+    // TODO init slots
+  }
+}
+
+export function renderEffectSimple(fn: () => void): void {
+  const updateFn = () => {
+    fn()
+  }
+  const effect = new ReactiveEffect(updateFn)
+  const job: SchedulerJob = effect.runIfDirty.bind(effect)
+  job.i = currentInstance as any
+  job.id = currentInstance!.uid
+  effect.scheduler = () => queueJob(job)
+  effect.run()
+
+  // TODO lifecycle
+  // TODO recurse handling
+  // TODO measure
+}
index fe57ab0efd223f302f3c2263fd1e4005f1c6c9ad..9cf898e518607565cb42d13b3daf6bf01c984e94 100644 (file)
@@ -29,7 +29,7 @@ export function setupComponent(instance: ComponentInternalInstance): void {
     startMeasure(instance, `init`)
   }
   const reset = setCurrentInstance(instance)
-  instance.scope.run(() => {
+  instance.scope.run(function componentSetupFn() {
     const { type: component, props } = instance
 
     if (__DEV__) {
index 89f8ccc881aadec891bf3c6fa39d33b127d9df9f..c91bcb77ce2ac15ace1cb06b6f8e988212d5cac8 100644 (file)
@@ -28,7 +28,10 @@ import type { Data } from '@vue/runtime-shared'
 
 export type Component = FunctionalComponent | ObjectComponent
 
-export type SetupFn = (props: any, ctx: SetupContext) => Block | Data | void
+export type SetupFn = (
+  props: any,
+  ctx: SetupContext,
+) => Block | Data | undefined
 export type FunctionalComponent = SetupFn &
   Omit<ObjectComponent, 'setup'> & {
     displayName?: string
index e3bd9ee5b24f86a22dbb32be9837b15246c10e5d..4aaf12dfe58b5dfa8cebf2765f73d557fc3a16c8 100644 (file)
@@ -3,14 +3,30 @@ import { renderEffect } from '../renderEffect'
 import { setText } from './prop'
 import { type Block, normalizeBlock } from '../block'
 
+// export function insert(
+//   block: Block,
+//   parent: ParentNode,
+//   anchor: Node | null = null,
+// ): void {
+//   const nodes = normalizeBlock(block)
+//   for (let i = 0; i < nodes.length; i++) {
+//     parent.insertBefore(nodes[i], anchor)
+//   }
+// }
+
 export function insert(
   block: Block,
   parent: ParentNode,
   anchor: Node | null = null,
 ): void {
-  const nodes = normalizeBlock(block)
-  for (let i = 0; i < nodes.length; i++) {
-    parent.insertBefore(nodes[i], anchor)
+  if (block instanceof Node) {
+    parent.insertBefore(block, anchor)
+  } else if (isArray(block)) {
+    for (let i = 0; i < block.length; i++) {
+      insert(block[i], parent, anchor)
+    }
+  } else if (block) {
+    insert(block.nodes, parent, anchor)
   }
 }
 
index c03548be334492462b2269779c048eb3ad34506f..fa84dd51485e132ee933b714005457e717259725 100644 (file)
@@ -77,7 +77,7 @@ export const warn = (__DEV__ ? _warn : NOOP) as typeof _warn
 export { nextTick } from './scheduler'
 export {
   getCurrentInstance,
-  type ComponentInternalInstance as ComponentInternalInstance,
+  ComponentInternalInstance,
   type Component as Component,
   type ObjectComponent,
   type FunctionalComponent,
@@ -155,6 +155,10 @@ export {
 export { createBranch, createIf } from './apiCreateIf'
 export { createFor, createForSlots } from './apiCreateFor'
 export { createComponent } from './apiCreateComponent'
+export {
+  createComponentSimple,
+  renderEffectSimple,
+} from './apiCreateComponentSimple'
 export { createSelector } from './apiCreateSelector'
 export { setInheritAttrs } from './componentAttrs'
 
index c826cd37bc41d1a899a39193ea2195856a320077..5795cf47e95b76c2ef0bfd7cb5295dbd423d55fe 100644 (file)
@@ -1,79 +1,8 @@
 <script setup lang="ts" vapor>
-import {
-  ref,
-  computed,
-  onMounted,
-  onBeforeMount,
-  getCurrentInstance,
-  onBeforeUpdate,
-  onUpdated,
-  onRenderTracked,
-  onRenderTriggered,
-} from 'vue/vapor'
-
-const instance = getCurrentInstance()!
-const count = ref(1)
-const double = computed(() => count.value * 2)
-const html = computed(() => `<button>HTML! ${count.value}</button>`)
-
-const inc = () => count.value++
-const dec = () => count.value--
-
-onBeforeMount(() => {
-  console.log('onBeforeMount', instance.isMounted)
-})
-onMounted(() => {
-  console.log('onMounted', instance.isMounted)
-})
-onMounted(() => {
-  setTimeout(() => {
-    count.value++
-  }, 1000)
-})
-
-onBeforeUpdate(() => {
-  console.log('before updated')
-})
-onUpdated(() => {
-  console.log('updated')
-})
-
-onRenderTracked(e => {
-  console.log(`Render Tracked:`, e.target)
-})
-onRenderTriggered(e => {
-  console.log(`Render trigger:`, e.target)
-})
-
-const log = (arg: any) => {
-  console.log('callback in render effect')
-  return arg
-}
+import Comp from './Comp.vue'
 </script>
 
 <template>
-  <div>
-    <h1 class="red">Counter</h1>
-    <div>The number is {{ log(count) }}.</div>
-    <div>{{ count }} * 2 = {{ double }}</div>
-    <div style="display: flex; gap: 8px">
-      <button @click="inc">inc</button>
-      <button @click="dec">dec</button>
-    </div>
-    <div v-html="html" />
-    <div v-text="html" />
-    <div v-once>once: {{ count }}</div>
-    <div v-pre>{{ count }}</div>
-    <div v-cloak>{{ count }}</div>
-  </div>
+  <h1>Vapor</h1>
+  <Comp />
 </template>
-
-<style>
-.red {
-  color: red;
-}
-
-html {
-  padding: 10px;
-}
-</style>
index d2999613d4234e246013279148f652b3251f2d0d..1c4ecdfc1ffdbf7894d3025955b051437fbc9d6d 100644 (file)
@@ -1,18 +1,79 @@
-/// <reference types="vite/client" />
+import {
+  createComponentSimple,
+  // createFor,
+  createVaporApp,
+  delegate,
+  delegateEvents,
+  ref,
+  renderEffectSimple,
+  template,
+} from 'vue/vapor'
 
-import { createVaporApp } from 'vue/vapor'
-import { createApp } from 'vue'
-import './style.css'
+function createForSimple(val: () => any, render: (i: number) => any) {
+  const l = val(),
+    arr = new Array(l)
+  for (let i = 0; i < l; i++) {
+    arr[i] = render(i)
+  }
+  return arr
+}
 
-const modules = import.meta.glob<any>('./**/*.(vue|js|ts)')
-const mod = (modules['.' + location.pathname] || modules['./App.vue'])()
+const t0 = template('<h1>Vapor</h1>')
+const App = {
+  vapor: true,
+  __name: 'App',
+  setup() {
+    return (_ctx => {
+      const n0 = t0()
+      const n1 = createForSimple(
+        () => 10000,
+        (i: number) => createComponentSimple(Comp, { count: i }),
+      )
+      return [n0, createComponentSimple(Counter), n1]
+    })()
+  },
+}
 
-mod.then(({ default: mod }) => {
-  const app = (mod.vapor ? createVaporApp : createApp)(mod)
-  app.mount('#app')
+const Counter = {
+  vapor: true,
+  __name: 'Counter',
+  setup() {
+    delegateEvents('click')
+    const count = ref(0)
+    const button = document.createElement('button')
+    button.textContent = '++'
+    delegate(button, 'click', () => () => count.value++)
+    return [
+      button,
+      createComponentSimple(Comp, {
+        // if ref
+        count,
+        // if exp
+        get plusOne() {
+          return count.value + 1
+        },
+      }),
+      // TODO dynamic props: merge with Proxy that iterates sources on access
+    ]
+  },
+}
 
-  // @ts-expect-error
-  globalThis.unmount = () => {
-    app.unmount()
-  }
-})
+const t0$1 = template('<div></div>')
+const Comp = {
+  vapor: true,
+  __name: 'Comp',
+  setup(props: any) {
+    return (_ctx => {
+      const n = t0$1()
+      renderEffectSimple(() => {
+        n.textContent = props.count + ' / ' + props.plusOne
+      })
+      return n
+    })()
+  },
+}
+
+const s = performance.now()
+const app = createVaporApp(App)
+app.mount('#app')
+console.log((performance.now() - s).toFixed(2))
index 7e80b8173138101d8de0b1c5f28d1ece827fb59d..2575d51fc0c3d2ddc505d9655eea61671c26c8f9 100644 (file)
@@ -5,7 +5,7 @@ import * as CompilerSFC from '@vue/compiler-sfc'
 export default defineConfig({
   build: {
     target: 'esnext',
-    minify: 'terser',
+    minify: false,
     terserOptions: {
       compress: {
         pure_getters: true,