diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.test.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.test.ts.snap index da4d0ad4c..9ad83079a 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/compile.test.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.test.ts.snap @@ -1,8 +1,7 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`comile > bindings 1`] = ` -"import { watchEffect } from 'vue'; -import { template, children, createTextNode, insert, setText } from 'vue/vapor'; +"import { template, children, createTextNode, insert, effect, setText } from 'vue/vapor'; const t0 = template('
count is .
'); export function render() { const n0 = t0(); @@ -16,7 +15,7 @@ export function render() { } = children(n0); const n1 = createTextNode(count.value); insert(n1, n3, n2); - watchEffect(() => { + effect(() => { setText(n1, undefined, count.value); }); return n0; @@ -25,15 +24,14 @@ export function render() { `; exports[`comile > directives > v-bind > simple expression 1`] = ` -"import { watchEffect } from 'vue'; -import { template, children, setAttr } from 'vue/vapor'; +"import { template, children, effect, setAttr } from 'vue/vapor'; const t0 = template('
'); export function render() { const n0 = t0(); const { 0: [n1], } = children(n0); - watchEffect(() => { + effect(() => { setAttr(n1, 'id', undefined, id.value); }); return n0; @@ -42,15 +40,14 @@ export function render() { `; exports[`comile > directives > v-html > no expression 1`] = ` -"import { watchEffect } from 'vue'; -import { template, children, setHtml } from 'vue/vapor'; +"import { template, children, effect, setHtml } from 'vue/vapor'; const t0 = template('
'); export function render() { const n0 = t0(); const { 0: [n1], } = children(n0); - watchEffect(() => { + effect(() => { setHtml(n1, undefined, ''); }); return n0; @@ -59,15 +56,14 @@ export function render() { `; exports[`comile > directives > v-html > simple expression 1`] = ` -"import { watchEffect } from 'vue'; -import { template, children, setHtml } from 'vue/vapor'; +"import { template, children, effect, setHtml } from 'vue/vapor'; const t0 = template('
'); export function render() { const n0 = t0(); const { 0: [n1], } = children(n0); - watchEffect(() => { + effect(() => { setHtml(n1, undefined, code.value); }); return n0; @@ -76,15 +72,14 @@ export function render() { `; exports[`comile > directives > v-on > simple expression 1`] = ` -"import { watchEffect } from 'vue'; -import { template, children, on } from 'vue/vapor'; +"import { template, children, effect, on } from 'vue/vapor'; const t0 = template('
'); export function render() { const n0 = t0(); const { 0: [n1], } = children(n0); - watchEffect(() => { + effect(() => { on(n1, 'click', handleClick); }); return n0; @@ -93,15 +88,14 @@ export function render() { `; exports[`comile > directives > v-once > as root node 1`] = ` -"import { watchEffect } from 'vue'; -import { template, children, setAttr } from 'vue/vapor'; +"import { template, children, effect, setAttr } from 'vue/vapor'; const t0 = template('
'); export function render() { const n0 = t0(); const { 0: [n1], } = children(n0); - watchEffect(() => { + effect(() => { setAttr(n1, 'id', undefined, foo); }); return n0; @@ -132,15 +126,14 @@ export function render() { `; exports[`comile > directives > v-text > no expression 1`] = ` -"import { watchEffect } from 'vue'; -import { template, children, setText } from 'vue/vapor'; +"import { template, children, effect, setText } from 'vue/vapor'; const t0 = template('
'); export function render() { const n0 = t0(); const { 0: [n1], } = children(n0); - watchEffect(() => { + effect(() => { setText(n1, undefined, ''); }); return n0; @@ -149,15 +142,14 @@ export function render() { `; exports[`comile > directives > v-text > simple expression 1`] = ` -"import { watchEffect } from 'vue'; -import { template, children, setText } from 'vue/vapor'; +"import { template, children, effect, setText } from 'vue/vapor'; const t0 = template('
'); export function render() { const n0 = t0(); const { 0: [n1], } = children(n0); - watchEffect(() => { + effect(() => { setText(n1, undefined, str.value); }); return n0; @@ -166,18 +158,17 @@ export function render() { `; exports[`comile > dynamic root 1`] = ` -"import { watchEffect } from 'vue'; -import { fragment, createTextNode, append, setText } from 'vue/vapor'; +"import { fragment, createTextNode, append, effect, setText } from 'vue/vapor'; export function render() { const t0 = fragment(); const n0 = t0(); const n1 = createTextNode(1); const n2 = createTextNode(2); append(n0, n1, n2); - watchEffect(() => { + effect(() => { setText(n1, undefined, 1); }); - watchEffect(() => { + effect(() => { setText(n2, undefined, 2); }); return n0; @@ -196,8 +187,7 @@ export function render() { `; exports[`comile > static + dynamic root 1`] = ` -"import { watchEffect } from 'vue'; -import { template, children, createTextNode, prepend, insert, append, setText } from 'vue/vapor'; +"import { template, children, createTextNode, prepend, insert, append, effect, setText } from 'vue/vapor'; const t0 = template('369'); export function render() { const n0 = t0(); @@ -217,28 +207,28 @@ export function render() { insert([n3, n4], n0, n9); insert([n5, n6], n0, n10); append(n0, n7, n8); - watchEffect(() => { + effect(() => { setText(n1, undefined, 1); }); - watchEffect(() => { + effect(() => { setText(n2, undefined, 2); }); - watchEffect(() => { + effect(() => { setText(n3, undefined, 4); }); - watchEffect(() => { + effect(() => { setText(n4, undefined, 5); }); - watchEffect(() => { + effect(() => { setText(n5, undefined, 7); }); - watchEffect(() => { + effect(() => { setText(n6, undefined, 8); }); - watchEffect(() => { + effect(() => { setText(n7, undefined, 'A'); }); - watchEffect(() => { + effect(() => { setText(n8, undefined, 'B'); }); return n0; diff --git a/packages/compiler-vapor/__tests__/__snapshots__/fixtures.test.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/fixtures.test.ts.snap index f90d49cf6..f2e8a3f99 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/fixtures.test.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/fixtures.test.ts.snap @@ -2,8 +2,7 @@ exports[`fixtures 1`] = ` "import { defineComponent as _defineComponent } from 'vue' -import { watchEffect } from 'vue' -import { template, children, createTextNode, append, setText, on, setHtml } from 'vue/vapor' +import { template, children, createTextNode, append, setText, effect, on, setHtml } from 'vue/vapor' const t0 = template(\\"

Counter

Count:

Double:

once:

{{ count }}

\\") import { ref, computed } from 'vue' @@ -28,16 +27,16 @@ append(n4, n3) const n7 = createTextNode(count.value) setText(n7, undefined, count.value) append(n8, n7) -watchEffect(() => { +effect(() => { setText(n1, undefined, count.value) }) -watchEffect(() => { +effect(() => { setText(n3, undefined, double.value) }) -watchEffect(() => { +effect(() => { on(n5, \\"click\\", increment) }) -watchEffect(() => { +effect(() => { setHtml(n6, undefined, html) }) return n0 diff --git a/packages/compiler-vapor/__tests__/compile.test.ts b/packages/compiler-vapor/__tests__/compile.test.ts index 979bb094f..7d0c1d774 100644 --- a/packages/compiler-vapor/__tests__/compile.test.ts +++ b/packages/compiler-vapor/__tests__/compile.test.ts @@ -129,7 +129,7 @@ describe('comile', () => { test.fails('as root node', async () => { const code = await compile(`
`) expect(code).toMatchSnapshot() - expect(code).not.contains('watchEffect') + expect(code).not.contains('effect') }) }) }) diff --git a/packages/compiler-vapor/src/generate.ts b/packages/compiler-vapor/src/generate.ts index f87f5fab5..0af9d764b 100644 --- a/packages/compiler-vapor/src/generate.ts +++ b/packages/compiler-vapor/src/generate.ts @@ -45,9 +45,8 @@ export function generate( code += genOperation(operation) } 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') + let scope = `effect(() => {\n` + vaporHelpers.add('effect') for (const operation of operations) { scope += genOperation(operation) } diff --git a/packages/runtime-vapor/package.json b/packages/runtime-vapor/package.json index 7015bd167..a2f218cbf 100644 --- a/packages/runtime-vapor/package.json +++ b/packages/runtime-vapor/package.json @@ -34,6 +34,8 @@ "url": "https://github.com/vuejs/core-vapor/issues" }, "homepage": "https://github.com/vuejs/core-vapor/tree/dev/packages/runtime-vapor#readme", - "dependencies": {}, - "devDependencies": {} + "dependencies": { + "@vue/shared": "workspace:*", + "@vue/reactivity": "workspace:*" + } } diff --git a/packages/runtime-vapor/src/index.ts b/packages/runtime-vapor/src/index.ts index 8553412a3..d6c95e437 100644 --- a/packages/runtime-vapor/src/index.ts +++ b/packages/runtime-vapor/src/index.ts @@ -1,3 +1,4 @@ export * from './on' export * from './render' export * from './template' +export * from './scheduler' diff --git a/packages/runtime-vapor/src/on.ts b/packages/runtime-vapor/src/on.ts index a1502a800..d4192b354 100644 --- a/packages/runtime-vapor/src/on.ts +++ b/packages/runtime-vapor/src/on.ts @@ -1,8 +1,8 @@ -export const on = ( +export function on( el: any, event: string, handler: () => any, options?: EventListenerOptions -) => { +) { el.addEventListener(event, handler, options) } diff --git a/packages/runtime-vapor/src/render.ts b/packages/runtime-vapor/src/render.ts index ed276c356..a120412fe 100644 --- a/packages/runtime-vapor/src/render.ts +++ b/packages/runtime-vapor/src/render.ts @@ -1,10 +1,10 @@ import { - effectScope, normalizeClass, normalizeStyle, - toDisplayString -} from 'vue' -import { isArray } from '@vue/shared' + toDisplayString, + isArray +} from '@vue/shared' +import { effectScope } from '@vue/reactivity' export type Block = Node | Fragment | Block[] export type ParentBlock = ParentNode | Node[] diff --git a/packages/runtime-vapor/src/scheduler.ts b/packages/runtime-vapor/src/scheduler.ts new file mode 100644 index 000000000..1af662e25 --- /dev/null +++ b/packages/runtime-vapor/src/scheduler.ts @@ -0,0 +1,30 @@ +import { ReactiveEffect } from '@vue/reactivity' + +const p = Promise.resolve() + +let queued: any[] | undefined + +const queue = (fn: any) => { + if (!queued) { + queued = [fn] + p.then(flush) + } else { + queued.push(fn) + } +} + +function flush() { + for (let i = 0; i < queued!.length; i++) { + queued![i]() + } + queued = undefined +} + +export const nextTick = (fn: any) => p.then(fn) + +export const effect = (fn: any) => { + let run: () => void + const e = new ReactiveEffect(fn, () => queue(run)) + run = e.run.bind(e) + run() +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4496dfcd9..ef2662c47 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -348,7 +348,14 @@ importers: specifier: workspace:* version: link:../shared - packages/runtime-vapor: {} + packages/runtime-vapor: + dependencies: + '@vue/reactivity': + specifier: workspace:* + version: link:../reactivity + '@vue/shared': + specifier: workspace:* + version: link:../shared packages/server-renderer: dependencies: