]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat: dynamic root node
author三咲智子 Kevin Deng <sxzz@sxzz.moe>
Fri, 24 Nov 2023 12:29:05 +0000 (20:29 +0800)
committer三咲智子 Kevin Deng <sxzz@sxzz.moe>
Fri, 24 Nov 2023 12:29:05 +0000 (20:29 +0800)
packages/compiler-vapor/__tests__/__snapshots__/compile.test.ts.snap
packages/compiler-vapor/__tests__/__snapshots__/fixtures.test.ts.snap
packages/compiler-vapor/__tests__/fixtures/counter.vue
packages/compiler-vapor/src/generate.ts
packages/compiler-vapor/src/transform.ts
playground/src/App-root.vue [new file with mode: 0644]
playground/src/main.ts

index b469ea52deb18b2cf747dfda7cfbb7936fe7062f..da9a78950b5c69f70cde7bcae8183ae359ae085b 100644 (file)
@@ -5,11 +5,12 @@ exports[`comile > bindings 1`] = `
 import { template, setText } from 'vue/vapor'
 const t0 = template(\`<div></div>\`)
 export function render() {
-const root = t0()
+const n0 = t0()
 watchEffect(() => {
 setText(n0, undefined, count.value)
 })
-return root
+return n0
+
 }"
 `;
 
@@ -18,11 +19,12 @@ exports[`comile > directives > v-bind > simple expression 1`] = `
 import { template, setAttr } from 'vue/vapor'
 const t0 = template(\`<div></div>\`)
 export function render() {
-const root = t0()
+const n0 = t0()
 watchEffect(() => {
 setAttr(n0, \\"id\\", undefined, id.value)
 })
-return root
+return n0
+
 }"
 `;
 
@@ -31,11 +33,12 @@ exports[`comile > directives > v-html > no expression 1`] = `
 import { template, setHtml } from 'vue/vapor'
 const t0 = template(\`<div></div>\`)
 export function render() {
-const root = t0()
+const n0 = t0()
 watchEffect(() => {
 setHtml(n0, undefined, \\"\\")
 })
-return root
+return n0
+
 }"
 `;
 
@@ -44,11 +47,12 @@ exports[`comile > directives > v-html > simple expression 1`] = `
 import { template, setHtml } from 'vue/vapor'
 const t0 = template(\`<div></div>\`)
 export function render() {
-const root = t0()
+const n0 = t0()
 watchEffect(() => {
 setHtml(n0, undefined, code.value)
 })
-return root
+return n0
+
 }"
 `;
 
@@ -57,11 +61,12 @@ exports[`comile > directives > v-on > simple expression 1`] = `
 import { template, on } from 'vue/vapor'
 const t0 = template(\`<div></div>\`)
 export function render() {
-const root = t0()
+const n0 = t0()
 watchEffect(() => {
 on(n0, \\"click\\", handleClick)
 })
-return root
+return n0
+
 }"
 `;
 
@@ -69,13 +74,14 @@ exports[`comile > directives > v-once 1`] = `
 "import { template, children, insert, setText, setAttr } from 'vue/vapor'
 const t0 = template(\`<div> <span></span></div>\`)
 export function render() {
-const root = t0()
-const { 1: [n2],} = children(root)
+const n0 = t0()
+const { 1: [n2],} = children(n0)
 const n1 = document.createTextNode(msg.value)
 insert(n1, n0, 0 /* InsertPosition.FIRST */)
 setText(n1, undefined, msg.value)
 setAttr(n2, \\"class\\", undefined, clz.value)
-return root
+return n0
+
 }"
 `;
 
@@ -84,11 +90,12 @@ exports[`comile > directives > v-text > no expression 1`] = `
 import { template, setText } from 'vue/vapor'
 const t0 = template(\`<div></div>\`)
 export function render() {
-const root = t0()
+const n0 = t0()
 watchEffect(() => {
 setText(n0, undefined, \\"\\")
 })
-return root
+return n0
+
 }"
 `;
 
@@ -97,11 +104,12 @@ exports[`comile > directives > v-text > simple expression 1`] = `
 import { template, setText } from 'vue/vapor'
 const t0 = template(\`<div></div>\`)
 export function render() {
-const root = t0()
+const n0 = t0()
 watchEffect(() => {
 setText(n0, undefined, str.value)
 })
-return root
+return n0
+
 }"
 `;
 
@@ -109,7 +117,8 @@ exports[`comile > static template 1`] = `
 "import { template } from 'vue/vapor'
 const t0 = template(\`<div><p>hello</p><input><span></span></div>\`)
 export function render() {
-const root = t0()
-return root
+const n0 = t0()
+return n0
+
 }"
 `;
index 4a4d7c6ced9d77c8abb2c4375953bfd8bd26f92e..fdf4d1cd9c628d301c4c427ee40e9c8bc108f0d7 100644 (file)
@@ -3,8 +3,8 @@
 exports[`fixtures 1`] = `
 "import { defineComponent as _defineComponent } from 'vue'
 import { watchEffect } from 'vue'
-import { template, insert, setText, on, setHtml } from 'vue/vapor'
-const t0 = template(\`<h1 id=\\"title\\">Counter</h1><p>Count: </p><p>Double: </p><button>Increment</button><div></div><input type=\\"text\\"><p>once: </p><p>{{ count }}</p>\`)
+import { template, children, insert, setText, on, setHtml } from 'vue/vapor'
+const t0 = template(\`<div><h1 id=\\"title\\">Counter</h1><p>Count: </p><p>Double: </p><button>Increment</button><div></div><input type=\\"text\\"><p>once: </p><p>{{ count }}</p></div>\`)
 import { ref, computed } from 'vue'
 
 const html = '<b>HTML</b>'
@@ -19,7 +19,8 @@ const increment = () => count.value++
 
 
 return (() => {
-const root = t0()
+const n8 = t0()
+const { 1: [n0], 2: [n2], 3: [n4], 4: [n5], 6: [n6],} = children(n8)
 const n1 = document.createTextNode(count.value)
 insert(n1, n0)
 const n3 = document.createTextNode(double.value)
@@ -39,7 +40,8 @@ on(n4, \\"click\\", increment)
 watchEffect(() => {
 setHtml(n5, undefined, html)
 })
-return root
+return n8
+
 })();
 }
 
index c780b459dec2d80316c4e6c2ba81679242bdbf65..d09cfd2e07349839b76bb5e334a3951083a8f827 100644 (file)
@@ -10,12 +10,14 @@ const html = '<b>HTML</b>'
 </script>
 
 <template>
-  <h1 id="title">Counter</h1>
-  <p>Count: {{ count }}</p>
-  <p>Double: {{ double }}</p>
-  <button @click="increment">Increment</button>
-  <div v-html="html" />
-  <input type="text" />
-  <p v-once>once: {{ count }}</p>
-  <p v-pre>{{ count }}</p>
+  <div>
+    <h1 id="title">Counter</h1>
+    <p>Count: {{ count }}</p>
+    <p>Double: {{ double }}</p>
+    <button @click="increment">Increment</button>
+    <div v-html="html" />
+    <input type="text" />
+    <p v-once>once: {{ count }}</p>
+    <p v-pre>{{ count }}</p>
+  </div>
 </template>
index e2ed01b73ead33c70f5aa14a124224053e8ac49d..ff4c497fff529d95914b8aa888e7c1529564b616 100644 (file)
@@ -27,29 +27,32 @@ export function generate(
     vaporHelpers.add('template')
   }
 
-  // TODO multiple-template
-  code += `const root = t0()\n`
-  if (ir.children[0] && Object.keys(ir.children[0].children).length) {
-    code += `const {${genChildren(ir.children[0].children)}} = children(root)\n`
-    vaporHelpers.add('children')
-  }
+  for (const [, { id, children }] of Object.entries(ir.children)) {
+    code += `const n${id} = t0()\n`
 
-  for (const operation of ir.operation) {
-    code += genOperation(operation)
-  }
+    if (Object.keys(children).length) {
+      code += `const {${genChildren(children)}} = children(n${id})\n`
+      vaporHelpers.add('children')
+    }
 
-  for (const [_expr, operations] of Object.entries(ir.effect)) {
-    // TODO don't use watchEffect from vue/core, implement `effect` function in runtime-vapor package
-    let scope = `watchEffect(() => {\n`
-    helpers.add('watchEffect')
-    for (const operation of operations) {
-      scope += genOperation(operation)
+    for (const operation of ir.operation) {
+      code += genOperation(operation)
     }
-    scope += '})\n'
-    code += scope
-  }
 
-  code += 'return root'
+    for (const [_expr, operations] of Object.entries(ir.effect)) {
+      // TODO don't use watchEffect from vue/core, implement `effect` function in runtime-vapor package
+      let scope = `watchEffect(() => {\n`
+      helpers.add('watchEffect')
+      for (const operation of operations) {
+        scope += genOperation(operation)
+      }
+      scope += '})\n'
+      code += scope
+    }
+
+    // TODO multiple-template
+    code += `return n${id}\n`
+  }
 
   if (vaporHelpers.size)
     preamble =
index 3ce7ff37525aec4b8fa4a8627302e7721d60f452..28a6eac7096dd30f393000b3fe7f37f9c6210b7d 100644 (file)
@@ -121,7 +121,7 @@ function createContext<T extends TemplateChildNode>(
       if (ctx.once) {
         return ctx.registerOpration(operation)
       }
-      parent.registerEffect(expr, operation)
+      return parent.registerEffect(expr, operation)
     },
   }
   return ctx
@@ -168,6 +168,9 @@ function transformChildren(
     const isFirst = i === 0
     const isLast = i === children.length - 1
 
+    // TODO: multiple root elements
+    if (root) child.store = true
+
     switch (node.type) {
       case 1 satisfies NodeTypes.ELEMENT: {
         transformElement(child as TransformContext<ElementNode>)
diff --git a/playground/src/App-root.vue b/playground/src/App-root.vue
new file mode 100644 (file)
index 0000000..74531c5
--- /dev/null
@@ -0,0 +1,20 @@
+<script setup lang="ts">
+import { ref } from 'vue'
+
+const count = ref(1)
+
+const handleClick = () => {
+  count.value++
+}
+
+// @ts-expect-error
+globalThis.count = count
+// @ts-expect-error
+globalThis.handleClick = handleClick
+</script>
+
+<template>
+  <button @click="handleClick">
+    {{ count }}
+  </button>
+</template>
index c458e0959189d3dd1bb1cb828f5b16331262532d..261042c33f648e958852771d740c4b28c9e125b2 100644 (file)
@@ -1,9 +1,11 @@
 import { render } from 'vue/vapor'
-import App from './App.vue'
 
-render(() => {
-  // @ts-expect-error
-  const returned = App.setup({}, { expose() {} })
-  // @ts-expect-error
-  return App.render(returned)
-}, '#app')
+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')
+})