From d435acbfb224dc2cbaff6412dd4f4dbc3c66b847 Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Mon, 4 Mar 2024 23:36:17 +0900 Subject: [PATCH 01/33] feat(runtime-vapor): component slot --- packages/runtime-vapor/src/component.ts | 3 + packages/runtime-vapor/src/componentSlots.ts | 21 ++++ packages/runtime-vapor/src/index.ts | 1 + packages/runtime-vapor/src/render.ts | 7 +- packages/runtime-vapor/src/slot.ts | 6 ++ playground/src/main.ts | 2 +- playground/src/slots.js | 100 +++++++++++++++++++ 7 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 packages/runtime-vapor/src/componentSlots.ts create mode 100644 packages/runtime-vapor/src/slot.ts create mode 100644 playground/src/slots.js diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index fce0a9e0d..6b7c4ad16 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -15,6 +15,7 @@ import { emit, normalizeEmitsOptions, } from './componentEmits' +import type { InternalSlots } from './componentSlots' import type { Data } from '@vue/shared' import { VaporLifecycleHooks } from './enums' @@ -56,6 +57,7 @@ export interface ComponentInternalInstance { setupState: Data emit: EmitFn emitted: Record<string, boolean> | null + slots: InternalSlots refs: Data vapor: true @@ -175,6 +177,7 @@ export const createComponentInstance = ( props: EMPTY_OBJ, attrs: EMPTY_OBJ, setupState: EMPTY_OBJ, + slots: EMPTY_OBJ, refs: EMPTY_OBJ, vapor: true, diff --git a/packages/runtime-vapor/src/componentSlots.ts b/packages/runtime-vapor/src/componentSlots.ts new file mode 100644 index 000000000..73521af5b --- /dev/null +++ b/packages/runtime-vapor/src/componentSlots.ts @@ -0,0 +1,21 @@ +import type { IfAny } from '@vue/shared' +import type { Block } from './render' +import type { ComponentInternalInstance } from './component' + +export type Slot<T extends any = any> = ( + ...args: IfAny<T, any[], [T] | (T extends undefined ? [] : never)> +) => Block[] + +export type InternalSlots = { + [name: string]: Slot | undefined +} + +export type Slots = Readonly<InternalSlots> + +export const initSlots = ( + instance: ComponentInternalInstance, + slots: Slots, +) => { + // TODO: normalize? + instance.slots = slots +} diff --git a/packages/runtime-vapor/src/index.ts b/packages/runtime-vapor/src/index.ts index 0cf0116b9..fa7a30b86 100644 --- a/packages/runtime-vapor/src/index.ts +++ b/packages/runtime-vapor/src/index.ts @@ -105,6 +105,7 @@ export { } from './apiLifecycle' export { createIf } from './if' export { createFor } from './for' +export { createSlots as createSlot } from './slot' // **Internal** DOM-only runtime directive helpers export { diff --git a/packages/runtime-vapor/src/render.ts b/packages/runtime-vapor/src/render.ts index e7425a4c3..fd8729503 100644 --- a/packages/runtime-vapor/src/render.ts +++ b/packages/runtime-vapor/src/render.ts @@ -14,6 +14,7 @@ import { unsetCurrentInstance, } from './component' import { initProps } from './componentProps' +import { type Slots, initSlots } from './componentSlots' import { invokeDirectiveHook } from './directives' import { insert, querySelector, remove } from './dom/element' import { flushPostFlushCbs, queuePostRenderEffect } from './scheduler' @@ -30,10 +31,12 @@ export type Fragment = { export function render( comp: Component, props: Data, + slots: Slots, // TODO: container: string | ParentNode, ): ComponentInternalInstance { const instance = createComponentInstance(comp, props) initProps(instance, props, !isFunction(instance.component)) + initSlots(instance, slots) const component = mountComponent( instance, (container = normalizeContainer(container)), @@ -56,8 +59,8 @@ function mountComponent( const reset = setCurrentInstance(instance) const block = instance.scope.run(() => { - const { component, props, emit, attrs } = instance - const ctx = { expose: () => {}, emit, attrs } + const { component, props, emit, attrs, slots } = instance + const ctx = { expose: () => {}, emit, attrs, slots } const setupFn = isFunction(component) ? component : component.setup const stateOrNode = setupFn && setupFn(props, ctx) diff --git a/packages/runtime-vapor/src/slot.ts b/packages/runtime-vapor/src/slot.ts new file mode 100644 index 000000000..46db4c631 --- /dev/null +++ b/packages/runtime-vapor/src/slot.ts @@ -0,0 +1,6 @@ +import type { Slots } from './componentSlots' + +// TODO: intercept? +export const createSlots = (slots: Slots) => { + return slots +} diff --git a/playground/src/main.ts b/playground/src/main.ts index d962dae1f..3d672a377 100644 --- a/playground/src/main.ts +++ b/playground/src/main.ts @@ -7,7 +7,7 @@ const mod = (modules['.' + location.pathname] || modules['./App.vue'])() mod.then(({ default: mod }) => { if (mod.vapor) { - const instance = render(mod, {}, '#app') + const instance = render(mod, {}, {}, '#app') // @ts-expect-error globalThis.unmount = () => { unmountComponent(instance) diff --git a/playground/src/slots.js b/playground/src/slots.js new file mode 100644 index 000000000..d8afd49df --- /dev/null +++ b/playground/src/slots.js @@ -0,0 +1,100 @@ +// @ts-check +import { + children, + createSlot, + defineComponent, + getCurrentInstance, + insert, + on, + ref, + render as renderComponent, + renderEffect, + setText, + template, +} from '@vue/vapor' + +const t0 = template('<div class="parnet-container"></div>') + +// <template #mySlot="{ message, changeMessage }"> +// <div clas="slotted"> +// <h1>{{ message }}</h1> +// <button @click="changeMessage">btn parent</button> +// </div> +// </template> +const t1 = template( + '<div class="slotted"><h1><!></h1><button>parent btn</button></div>', +) + +const Parent = defineComponent({ + vapor: true, + props: undefined, + setup(props) {}, + render(_ctx) { + const n0 = /** @type {any} */ (t0()) + const s0 = createSlot({ + mySlot: scope => { + const n1 = t1() + const n2 = /** @type {any} */ (children(n1, 0)) + const n3 = /** @type {any} */ (children(n1, 1)) + renderEffect(() => { + setText(n2, scope.message) + }) + on(n3, 'click', scope.changeMessage) + return [n1] + }, + // e.g. default slot + // default: () => { + // const n1 = t1() + // return [n1] + // } + }) + renderComponent(Child, {}, s0, n0) + return n0 + }, +}) + +const t2 = template( + '<div class="child-container"><button>child btn</button></div>', +) + +const Child = defineComponent({ + vapor: true, + props: undefined, + setup(props, { expose: __expose }) { + __expose() + const message = ref('Hello World!') + function changeMessage() { + message.value += '!' + } + const __returned__ = { message, changeMessage } + Object.defineProperty(__returned__, '__isScriptSetup', { + enumerable: false, + value: true, + }) + return __returned__ + }, + render(_ctx) { + const instance = /** @type {any} */ (getCurrentInstance()) + const slots = instance.slots + + // <div> + // <slot name="mySlot" :message="msg" :changeMessage="changeMessage" /> + // <button @click="changeMessage">button in child</button> + // </div> + const n0 = /** @type {any} */ (t2()) + const n1 = /** @type {any} */ (children(n0, 0)) + on(n1, 'click', _ctx.changeMessage) + const mySlot = slots.mySlot({ + get message() { + return _ctx.message + }, + get changeMessage() { + return _ctx.changeMessage + }, + }) + insert(mySlot, n0, n1) + return n0 + }, +}) + +export default Parent From 61ada62d7e0d58f931fdfd449042ab1beadd264d Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Mon, 4 Mar 2024 23:46:07 +0900 Subject: [PATCH 02/33] chore: render fn arg --- packages/runtime-vapor/__tests__/_utils.ts | 4 +++- packages/runtime-vapor/__tests__/componentProps.spec.ts | 1 + playground/src/props.js | 3 +-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/runtime-vapor/__tests__/_utils.ts b/packages/runtime-vapor/__tests__/_utils.ts index d4501273d..17adc4c84 100644 --- a/packages/runtime-vapor/__tests__/_utils.ts +++ b/packages/runtime-vapor/__tests__/_utils.ts @@ -6,6 +6,7 @@ import { render as _render, defineComponent, } from '../src' +import type { Slots } from '../src/componentSlots' export function makeRender<Component = ObjectComponent | SetupFn>( initHost = () => { @@ -28,9 +29,10 @@ export function makeRender<Component = ObjectComponent | SetupFn>( let instance: ComponentInternalInstance const render = ( props: Data = {}, + slots: Slots = {}, container: string | ParentNode = '#host', ) => { - instance = _render(component, props, container) + instance = _render(component, props, slots, container) return res() } const res = () => ({ diff --git a/packages/runtime-vapor/__tests__/componentProps.spec.ts b/packages/runtime-vapor/__tests__/componentProps.spec.ts index df493d71d..72eff574c 100644 --- a/packages/runtime-vapor/__tests__/componentProps.spec.ts +++ b/packages/runtime-vapor/__tests__/componentProps.spec.ts @@ -316,6 +316,7 @@ describe('component props (vapor)', () => { return _ctx.id }, }, + {}, n0 as HTMLDivElement, ) return n0 diff --git a/playground/src/props.js b/playground/src/props.js index 8da476cc8..10960ef90 100644 --- a/playground/src/props.js +++ b/playground/src/props.js @@ -46,8 +46,6 @@ export default defineComponent({ // insert(n0, c0) renderComponent( /** @type {any} */ (child), - - // TODO: proxy?? { /* <Comp :count="count" /> */ get count() { @@ -59,6 +57,7 @@ export default defineComponent({ return _ctx.count * 2 }, }, + {}, // @ts-expect-error TODO n0[0], ) From f4768bc536b178254307eadebe0abeb093f2ff47 Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Mon, 4 Mar 2024 23:48:39 +0900 Subject: [PATCH 03/33] chore playground --- playground/src/slots.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/playground/src/slots.js b/playground/src/slots.js index d8afd49df..880ad57cb 100644 --- a/playground/src/slots.js +++ b/playground/src/slots.js @@ -75,7 +75,7 @@ const Child = defineComponent({ }, render(_ctx) { const instance = /** @type {any} */ (getCurrentInstance()) - const slots = instance.slots + const { slots } = instance // <div> // <slot name="mySlot" :message="msg" :changeMessage="changeMessage" /> @@ -84,7 +84,7 @@ const Child = defineComponent({ const n0 = /** @type {any} */ (t2()) const n1 = /** @type {any} */ (children(n0, 0)) on(n1, 'click', _ctx.changeMessage) - const mySlot = slots.mySlot({ + const s0 = slots.mySlot({ get message() { return _ctx.message }, @@ -92,7 +92,7 @@ const Child = defineComponent({ return _ctx.changeMessage }, }) - insert(mySlot, n0, n1) + insert(s0, n0, n1) return n0 }, }) From ffee6720e72475eaeff9adf46a26ceb87317b256 Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sun, 10 Mar 2024 18:41:38 +0900 Subject: [PATCH 04/33] test(runtime-vapor): component slots (def test cases) --- .../__tests__/componentSlots.spec.ts | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 packages/runtime-vapor/__tests__/componentSlots.spec.ts diff --git a/packages/runtime-vapor/__tests__/componentSlots.spec.ts b/packages/runtime-vapor/__tests__/componentSlots.spec.ts new file mode 100644 index 000000000..5b1a7b9e4 --- /dev/null +++ b/packages/runtime-vapor/__tests__/componentSlots.spec.ts @@ -0,0 +1,43 @@ +// NOTE: This test is implemented based on the case of `runtime-core/__test__/componentSlots.spec.ts`. + +import { makeRender } from './_utils' + +const define = makeRender<any>() + +describe('component: slots', () => { + test.todo('initSlots: instance.slots should be set correctly', () => {}) + + test.todo( + 'initSlots: should normalize object slots (when value is null, string, array)', + () => {}, + ) + + test.todo( + 'initSlots: should normalize object slots (when value is function)', + () => {}, + ) + + test.todo( + 'initSlots: instance.slots should be set correctly (when vnode.shapeFlag is not SLOTS_CHILDREN)', + () => {}, + ) + + test.todo( + 'updateSlots: instance.slots should be updated correctly (when slotType is number)', + async () => {}, + ) + + test.todo( + 'updateSlots: instance.slots should be updated correctly (when slotType is null)', + async () => {}, + ) + + test.todo( + 'updateSlots: instance.slots should be update correctly (when vnode.shapeFlag is not SLOTS_CHILDREN)', + async () => {}, + ) + + test.todo('should respect $stable flag', async () => {}) + + test.todo('should not warn when mounting another app in setup', () => {}) +}) From 246badc0769d931a7e9aa122fb261d1308a6149b Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Mon, 11 Mar 2024 00:04:13 +0900 Subject: [PATCH 05/33] test(runtime-vapor): component slots [wip] --- .../__tests__/componentSlots.spec.ts | 234 ++++++++++++++++-- 1 file changed, 220 insertions(+), 14 deletions(-) diff --git a/packages/runtime-vapor/__tests__/componentSlots.spec.ts b/packages/runtime-vapor/__tests__/componentSlots.spec.ts index 5b1a7b9e4..4c67102fa 100644 --- a/packages/runtime-vapor/__tests__/componentSlots.spec.ts +++ b/packages/runtime-vapor/__tests__/componentSlots.spec.ts @@ -1,43 +1,249 @@ // NOTE: This test is implemented based on the case of `runtime-core/__test__/componentSlots.spec.ts`. +import { + defineComponent, + getCurrentInstance, + nextTick, + ref, + render as renderComponent, + template, +} from '../src' +import { createSlots } from '../src/slot' import { makeRender } from './_utils' const define = makeRender<any>() describe('component: slots', () => { - test.todo('initSlots: instance.slots should be set correctly', () => {}) + function renderWithSlots(slots: any): any { + let instance: any + const Comp = defineComponent({ + vapor: true, + render() { + const t0 = template('<div></div>') + const n0 = t0() + instance = getCurrentInstance() + return n0 + }, + }) + + const { render } = define({ + render() { + const t0 = template('<div></div>') + const n0 = t0() + renderComponent(Comp, {}, slots, n0 as ParentNode) + }, + }) + + render() + return instance + } + + test('initSlots: instance.slots should be set correctly', () => { + const { slots } = renderWithSlots({ _: 1 }) + expect(slots).toMatchObject({ _: 1 }) + }) test.todo( 'initSlots: should normalize object slots (when value is null, string, array)', - () => {}, + () => { + // TODO: normalize + }, ) test.todo( 'initSlots: should normalize object slots (when value is function)', - () => {}, + () => { + // TODO: normalize + }, ) - test.todo( - 'initSlots: instance.slots should be set correctly (when vnode.shapeFlag is not SLOTS_CHILDREN)', - () => {}, - ) + test('initSlots: instance.slots should be set correctly', () => { + let proxy: any + const { render } = define({ + render() { + const t0 = template('<div></div>') + const n0 = t0() + proxy = getCurrentInstance() + return n0 + }, + }) + + render( + {}, + createSlots({ + header: () => { + const t0 = template('header') + // TODO: single node + return [t0()] + }, + }), + ) + expect(proxy.slots.header()).toMatchObject([ + document.createTextNode('header'), + ]) + }) + test('initSlots: instance.slots should be set correctly (when vnode.shapeFlag is not SLOTS_CHILDREN)', () => { + const { slots } = renderWithSlots( + createSlots({ + // TODO: normalize from array + default: () => { + const t0 = template('<span></span>') + return [t0()] + }, + }), + ) + + // TODO: warn + // expect( + // '[Vue warn]: Non-function value encountered for default slot. Prefer function slots for better performance.', + // ).toHaveBeenWarned() + + expect(slots.default()).toMatchObject([document.createElement('span')]) + }) + + // TODO: dynamic slot test.todo( - 'updateSlots: instance.slots should be updated correctly (when slotType is number)', - async () => {}, + 'updateSlots: instance.slots should be updated correctly', + async () => { + const flag1 = ref(true) + + let instance: any + const Child = () => { + instance = getCurrentInstance() + return template('child')() + } + + const { render } = define({ + render() { + const t0 = template('<div></div>') + const n0 = t0() + renderComponent( + Child, + {}, + createSlots({ + default: () => { + // TODO: dynamic slot + return flag1.value + ? [template('<span></span>')()] + : [template('<div></div>')()] + }, + }), + n0 as ParentNode, + ) + return [] + }, + }) + + render() + + expect(instance.slots.default()).toMatchObject([]) + expect(instance.slots.default()).not.toMatchObject([]) + + flag1.value = false + await nextTick() + + expect(instance.slots.default()).not.toMatchObject([]) + expect(instance.slots.default()).toMatchObject([]) + }, ) + // TODO: dynamic slots test.todo( - 'updateSlots: instance.slots should be updated correctly (when slotType is null)', - async () => {}, + 'updateSlots: instance.slots should be updated correctly', + async () => { + const flag1 = ref(true) + + let instance: any + const Child = () => { + instance = getCurrentInstance() + return template('child')() + } + + const oldSlots = { + header: () => template('header')(), + footer: undefined, + } + const newSlots = { + header: undefined, + footer: () => template('footer')(), + } + + const { render } = define({ + render() { + const t0 = template('<div></div>') + const n0 = t0() + // renderComponent( + // Child, + // {}, + // createSlots(flag1.value ? oldSlots : newSlots), + // n0 as ParentNode, + // ) + return [] + }, + }) + + render() + + expect(instance.slots).toMatchObject({ _: null }) + + flag1.value = false + await nextTick() + + expect(instance.slots).toMatchObject({ _: null }) + }, ) test.todo( 'updateSlots: instance.slots should be update correctly (when vnode.shapeFlag is not SLOTS_CHILDREN)', - async () => {}, + async () => { + // TODO: dynamic slots + }, ) - test.todo('should respect $stable flag', async () => {}) + test.todo('should respect $stable flag', async () => { + // TODO: $stable flag + }) - test.todo('should not warn when mounting another app in setup', () => {}) + test.todo('should not warn when mounting another app in setup', () => { + // TODO: warning and createApp fn + // const Comp = { + // render() { + // const i = getCurrentInstance() + // return i?.slots.default?.() + // }, + // } + // const mountComp = () => { + // createApp({ + // render() { + // const t0 = template('<div></div>') + // const n0 = t0() + // renderComponent( + // Comp, + // {}, + // createSlots({ + // default: () => { + // const t0 = template('msg') + // return [t0()] + // }, + // }), + // n0, + // ) + // return n0 + // }, + // }) + // } + // const App = { + // setup() { + // mountComp() + // }, + // render() { + // return null + // }, + // } + // createApp(App).mount(document.createElement('div')) + // expect( + // 'Slot "default" invoked outside of the render function', + // ).not.toHaveBeenWarned() + }) }) From 60edb08528d1747b36e48fe669217f5d3d1eea6d Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sun, 17 Mar 2024 16:19:05 +0900 Subject: [PATCH 06/33] chore: add comment --- packages/runtime-vapor/src/componentSlots.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/runtime-vapor/src/componentSlots.ts b/packages/runtime-vapor/src/componentSlots.ts index 73521af5b..f2164d2a2 100644 --- a/packages/runtime-vapor/src/componentSlots.ts +++ b/packages/runtime-vapor/src/componentSlots.ts @@ -19,3 +19,5 @@ export const initSlots = ( // TODO: normalize? instance.slots = slots } + +// TODO: $stable ? From d6903315a844659b77e772bccdbdca8090e9dbdf Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sun, 17 Mar 2024 17:07:41 +0900 Subject: [PATCH 07/33] chore: merge refactoring of component --- .../runtime-vapor/src/apiCreateComponent.ts | 9 +- packages/runtime-vapor/src/component.ts | 5 +- packages/runtime-vapor/src/componentSlots.ts | 5 +- playground/src/slots.js | 99 +++++++++---------- 4 files changed, 58 insertions(+), 60 deletions(-) diff --git a/packages/runtime-vapor/src/apiCreateComponent.ts b/packages/runtime-vapor/src/apiCreateComponent.ts index c74783b32..4d40f0e66 100644 --- a/packages/runtime-vapor/src/apiCreateComponent.ts +++ b/packages/runtime-vapor/src/apiCreateComponent.ts @@ -5,10 +5,15 @@ import { } from './component' import { setupComponent } from './apiRender' import type { RawProps } from './componentProps' +import type { Slots } from './componentSlots' -export function createComponent(comp: Component, rawProps: RawProps = null) { +export function createComponent( + comp: Component, + rawProps: RawProps = null, + slots: Slots | null = null, +) { const current = currentInstance! - const instance = createComponentInstance(comp, rawProps) + const instance = createComponentInstance(comp, rawProps, slots) setupComponent(instance) // register sub-component with current component for lifecycle management diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 9aa512bf3..54c755754 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -17,7 +17,7 @@ import { emit, normalizeEmitsOptions, } from './componentEmits' -import type { InternalSlots } from './componentSlots' +import { type InternalSlots, type Slots, initSlots } from './componentSlots' import { VaporLifecycleHooks } from './apiLifecycle' import type { Data } from '@vue/shared' @@ -142,6 +142,7 @@ let uid = 0 export function createComponentInstance( component: ObjectComponent | FunctionalComponent, rawProps: RawProps | null, + slots: Slots | null, ): ComponentInternalInstance { const instance: ComponentInternalInstance = { uid: uid++, @@ -228,7 +229,7 @@ export function createComponentInstance( // [VaporLifecycleHooks.SERVER_PREFETCH]: null, } initProps(instance, rawProps, !isFunction(component)) - // TODO: initSlots(instance, slots) + initSlots(instance, slots) instance.emit = emit.bind(null, instance) return instance diff --git a/packages/runtime-vapor/src/componentSlots.ts b/packages/runtime-vapor/src/componentSlots.ts index f2164d2a2..5bacab2f0 100644 --- a/packages/runtime-vapor/src/componentSlots.ts +++ b/packages/runtime-vapor/src/componentSlots.ts @@ -1,6 +1,6 @@ import type { IfAny } from '@vue/shared' -import type { Block } from './render' import type { ComponentInternalInstance } from './component' +import type { Block } from './apiRender' export type Slot<T extends any = any> = ( ...args: IfAny<T, any[], [T] | (T extends undefined ? [] : never)> @@ -14,8 +14,9 @@ export type Slots = Readonly<InternalSlots> export const initSlots = ( instance: ComponentInternalInstance, - slots: Slots, + slots: Slots | null, ) => { + if (!slots) slots = {} // TODO: normalize? instance.slots = slots } diff --git a/playground/src/slots.js b/playground/src/slots.js index 880ad57cb..2cd6982b1 100644 --- a/playground/src/slots.js +++ b/playground/src/slots.js @@ -1,13 +1,13 @@ // @ts-check import { children, - createSlot, + createComponent, + createSlots, defineComponent, getCurrentInstance, insert, on, ref, - render as renderComponent, renderEffect, setText, template, @@ -27,29 +27,31 @@ const t1 = template( const Parent = defineComponent({ vapor: true, - props: undefined, - setup(props) {}, - render(_ctx) { - const n0 = /** @type {any} */ (t0()) - const s0 = createSlot({ - mySlot: scope => { - const n1 = t1() - const n2 = /** @type {any} */ (children(n1, 0)) - const n3 = /** @type {any} */ (children(n1, 1)) - renderEffect(() => { - setText(n2, scope.message) - }) - on(n3, 'click', scope.changeMessage) - return [n1] - }, - // e.g. default slot - // default: () => { - // const n1 = t1() - // return [n1] - // } - }) - renderComponent(Child, {}, s0, n0) - return n0 + + setup(props) { + return (() => { + const n0 = /** @type {any} */ (t0()) + const s0 = createSlots({ + mySlot: scope => { + const n1 = t1() + const n2 = /** @type {any} */ (children(n1, 0)) + const n3 = /** @type {any} */ (children(n1, 1)) + renderEffect(() => { + setText(n2, scope.message()) + }) + on(n3, 'click', scope.changeMessage) + return [n1] + }, + // e.g. default slot + // default: () => { + // const n1 = t1() + // return [n1] + // } + }) + /** @type {any} */ + const n1 = createComponent(Child, {}, s0) + return [n0, n1] + })() }, }) @@ -59,41 +61,30 @@ const t2 = template( const Child = defineComponent({ vapor: true, - props: undefined, - setup(props, { expose: __expose }) { + setup(_, { expose: __expose }) { __expose() const message = ref('Hello World!') function changeMessage() { message.value += '!' } - const __returned__ = { message, changeMessage } - Object.defineProperty(__returned__, '__isScriptSetup', { - enumerable: false, - value: true, - }) - return __returned__ - }, - render(_ctx) { - const instance = /** @type {any} */ (getCurrentInstance()) - const { slots } = instance - // <div> - // <slot name="mySlot" :message="msg" :changeMessage="changeMessage" /> - // <button @click="changeMessage">button in child</button> - // </div> - const n0 = /** @type {any} */ (t2()) - const n1 = /** @type {any} */ (children(n0, 0)) - on(n1, 'click', _ctx.changeMessage) - const s0 = slots.mySlot({ - get message() { - return _ctx.message - }, - get changeMessage() { - return _ctx.changeMessage - }, - }) - insert(s0, n0, n1) - return n0 + return (() => { + const instance = /** @type {any} */ (getCurrentInstance()) + const { slots } = instance + // <div> + // <slot name="mySlot" :message="msg" :changeMessage="changeMessage" /> + // <button @click="changeMessage">button in child</button> + // </div> + const n0 = /** @type {any} */ (t2()) + const n1 = /** @type {any} */ (children(n0, 0)) + on(n1, 'click', () => changeMessage) + const s0 = slots.mySlot({ + message: () => message.value, + changeMessage: () => changeMessage, + }) + insert(s0, n0, n1) + return n0 + })() }, }) From e967ed17de1635065d607d75cae83d13a2fabf69 Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sun, 17 Mar 2024 17:13:48 +0900 Subject: [PATCH 08/33] chore: fix test --- packages/runtime-vapor/__tests__/_utils.ts | 2 +- .../__tests__/componentSlots.spec.ts | 18 ++++++------------ .../runtime-vapor/src/apiCreateVaporApp.ts | 2 +- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/packages/runtime-vapor/__tests__/_utils.ts b/packages/runtime-vapor/__tests__/_utils.ts index 0fa8d92bb..befb3dff3 100644 --- a/packages/runtime-vapor/__tests__/_utils.ts +++ b/packages/runtime-vapor/__tests__/_utils.ts @@ -34,7 +34,7 @@ export function makeRender<Component = ObjectComponent | SetupFn>( slots: Slots = {}, container: string | ParentNode = '#host', ) => { - app = createVaporApp(component, props) + app = createVaporApp(component, props, slots) instance = app.mount(container) return res() diff --git a/packages/runtime-vapor/__tests__/componentSlots.spec.ts b/packages/runtime-vapor/__tests__/componentSlots.spec.ts index 4c67102fa..625af8684 100644 --- a/packages/runtime-vapor/__tests__/componentSlots.spec.ts +++ b/packages/runtime-vapor/__tests__/componentSlots.spec.ts @@ -1,11 +1,11 @@ // NOTE: This test is implemented based on the case of `runtime-core/__test__/componentSlots.spec.ts`. import { + createComponent, defineComponent, getCurrentInstance, nextTick, ref, - render as renderComponent, template, } from '../src' import { createSlots } from '../src/slot' @@ -28,9 +28,7 @@ describe('component: slots', () => { const { render } = define({ render() { - const t0 = template('<div></div>') - const n0 = t0() - renderComponent(Comp, {}, slots, n0 as ParentNode) + return createComponent(Comp, {}, slots) }, }) @@ -58,12 +56,12 @@ describe('component: slots', () => { ) test('initSlots: instance.slots should be set correctly', () => { - let proxy: any + let instance: any const { render } = define({ render() { const t0 = template('<div></div>') const n0 = t0() - proxy = getCurrentInstance() + instance = getCurrentInstance() return n0 }, }) @@ -78,7 +76,7 @@ describe('component: slots', () => { }, }), ) - expect(proxy.slots.header()).toMatchObject([ + expect(instance.slots.header()).toMatchObject([ document.createTextNode('header'), ]) }) @@ -116,9 +114,7 @@ describe('component: slots', () => { const { render } = define({ render() { - const t0 = template('<div></div>') - const n0 = t0() - renderComponent( + return createComponent( Child, {}, createSlots({ @@ -129,9 +125,7 @@ describe('component: slots', () => { : [template('<div></div>')()] }, }), - n0 as ParentNode, ) - return [] }, }) diff --git a/packages/runtime-vapor/src/apiCreateVaporApp.ts b/packages/runtime-vapor/src/apiCreateVaporApp.ts index 31dacf356..084514fb6 100644 --- a/packages/runtime-vapor/src/apiCreateVaporApp.ts +++ b/packages/runtime-vapor/src/apiCreateVaporApp.ts @@ -40,7 +40,7 @@ export function createVaporApp( mount(rootContainer): any { if (!instance) { - instance = createComponentInstance(rootComponent, rootProps) + instance = createComponentInstance(rootComponent, rootProps, rootSlots) setupComponent(instance) render(instance, rootContainer) return instance From fd2d9ff092c3795f750581d66bf02929643ef864 Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sun, 17 Mar 2024 19:25:14 +0900 Subject: [PATCH 09/33] feat(runtime-vapor): dynamic slots --- .../__tests__/componentSlots.spec.ts | 221 +++++++++--------- packages/runtime-vapor/src/apiCreateSlots.ts | 65 ++++++ packages/runtime-vapor/src/componentSlots.ts | 6 +- packages/runtime-vapor/src/index.ts | 2 +- packages/runtime-vapor/src/slot.ts | 6 - 5 files changed, 182 insertions(+), 118 deletions(-) create mode 100644 packages/runtime-vapor/src/apiCreateSlots.ts delete mode 100644 packages/runtime-vapor/src/slot.ts diff --git a/packages/runtime-vapor/__tests__/componentSlots.spec.ts b/packages/runtime-vapor/__tests__/componentSlots.spec.ts index 625af8684..8484482cf 100644 --- a/packages/runtime-vapor/__tests__/componentSlots.spec.ts +++ b/packages/runtime-vapor/__tests__/componentSlots.spec.ts @@ -2,13 +2,14 @@ import { createComponent, + createVaporApp, defineComponent, getCurrentInstance, nextTick, ref, template, } from '../src' -import { createSlots } from '../src/slot' +import { createSlots } from '../src/apiCreateSlots' import { makeRender } from './_utils' const define = makeRender<any>() @@ -69,80 +70,65 @@ describe('component: slots', () => { render( {}, createSlots({ - header: () => { - const t0 = template('header') - // TODO: single node - return [t0()] - }, + header: () => template('header')(), }), ) - expect(instance.slots.header()).toMatchObject([ + expect(instance.slots.header()).toMatchObject( document.createTextNode('header'), - ]) + ) }) + // TODO: test case name test('initSlots: instance.slots should be set correctly (when vnode.shapeFlag is not SLOTS_CHILDREN)', () => { const { slots } = renderWithSlots( createSlots({ - // TODO: normalize from array - default: () => { - const t0 = template('<span></span>') - return [t0()] - }, + // TODO: normalize from array? + default: () => template('<span></span>')(), }), ) - // TODO: warn // expect( // '[Vue warn]: Non-function value encountered for default slot. Prefer function slots for better performance.', // ).toHaveBeenWarned() - expect(slots.default()).toMatchObject([document.createElement('span')]) + expect(slots.default()).toMatchObject(document.createElement('span')) }) - // TODO: dynamic slot - test.todo( - 'updateSlots: instance.slots should be updated correctly', - async () => { - const flag1 = ref(true) + test('updateSlots: instance.slots should be updated correctly', async () => { + const flag1 = ref(true) - let instance: any - const Child = () => { - instance = getCurrentInstance() - return template('child')() - } + let instance: any + const Child = () => { + instance = getCurrentInstance() + return template('child')() + } - const { render } = define({ - render() { - return createComponent( - Child, - {}, - createSlots({ - default: () => { - // TODO: dynamic slot - return flag1.value - ? [template('<span></span>')()] - : [template('<div></div>')()] - }, - }), - ) - }, - }) + const { render } = define({ + render() { + return createComponent( + Child, + {}, + createSlots({ _: 2 as any }, () => [ + flag1.value + ? { name: 'one', fn: () => template('<span></span>')() } + : { name: 'two', fn: () => template('<div></div>')() }, + ]), + ) + }, + }) - render() + render() - expect(instance.slots.default()).toMatchObject([]) - expect(instance.slots.default()).not.toMatchObject([]) + expect(instance.slots).toHaveProperty('one') + expect(instance.slots).not.toHaveProperty('two') - flag1.value = false - await nextTick() + flag1.value = false + await nextTick() - expect(instance.slots.default()).not.toMatchObject([]) - expect(instance.slots.default()).toMatchObject([]) - }, - ) + expect(instance.slots).not.toHaveProperty('one') + expect(instance.slots).toHaveProperty('two') + }) - // TODO: dynamic slots test.todo( 'updateSlots: instance.slots should be updated correctly', async () => { @@ -164,16 +150,15 @@ describe('component: slots', () => { } const { render } = define({ - render() { - const t0 = template('<div></div>') - const n0 = t0() - // renderComponent( - // Child, - // {}, - // createSlots(flag1.value ? oldSlots : newSlots), - // n0 as ParentNode, - // ) - return [] + setup() { + return (() => { + return createComponent( + Child, + {}, + // TODO: maybe it is not supported + createSlots(flag1.value ? oldSlots : newSlots), + ) + })() }, }) @@ -188,56 +173,78 @@ describe('component: slots', () => { }, ) - test.todo( - 'updateSlots: instance.slots should be update correctly (when vnode.shapeFlag is not SLOTS_CHILDREN)', - async () => { - // TODO: dynamic slots - }, - ) + // TODO: test case name + test('updateSlots: instance.slots should be update correctly (when vnode.shapeFlag is not SLOTS_CHILDREN)', async () => { + const flag1 = ref(true) + + let instance: any + const Child = () => { + instance = getCurrentInstance() + return template('child')() + } + + const { render } = define({ + setup() { + return createComponent( + Child, + {}, + createSlots({}, () => [ + flag1.value + ? [{ name: 'header', fn: () => template('header')() }] + : [{ name: 'footer', fn: () => template('footer')() }], + ]), + ) + }, + }) + render() + + expect(instance.slots).toHaveProperty('header') + flag1.value = false + await nextTick() + + // expect( + // '[Vue warn]: Non-function value encountered for default slot. Prefer function slots for better performance.', + // ).toHaveBeenWarned() + + expect(instance.slots).toHaveProperty('footer') + }) test.todo('should respect $stable flag', async () => { - // TODO: $stable flag + // TODO: $stable flag? }) test.todo('should not warn when mounting another app in setup', () => { - // TODO: warning and createApp fn - // const Comp = { - // render() { - // const i = getCurrentInstance() - // return i?.slots.default?.() - // }, - // } - // const mountComp = () => { - // createApp({ - // render() { - // const t0 = template('<div></div>') - // const n0 = t0() - // renderComponent( - // Comp, - // {}, - // createSlots({ - // default: () => { - // const t0 = template('msg') - // return [t0()] - // }, - // }), - // n0, - // ) - // return n0 - // }, - // }) - // } - // const App = { - // setup() { - // mountComp() - // }, - // render() { - // return null - // }, - // } - // createApp(App).mount(document.createElement('div')) - // expect( - // 'Slot "default" invoked outside of the render function', - // ).not.toHaveBeenWarned() + // TODO: warning + const Comp = defineComponent({ + render() { + const i = getCurrentInstance() + return i!.slots.default!() + }, + }) + const mountComp = () => { + createVaporApp({ + render() { + return createComponent( + Comp, + {}, + createSlots({ + default: () => template('msg')(), + }), + )! + }, + }) + } + const App = { + setup() { + mountComp() + }, + render() { + return null! + }, + } + createVaporApp(App).mount(document.createElement('div')) + expect( + 'Slot "default" invoked outside of the render function', + ).not.toHaveBeenWarned() }) }) diff --git a/packages/runtime-vapor/src/apiCreateSlots.ts b/packages/runtime-vapor/src/apiCreateSlots.ts new file mode 100644 index 000000000..af619da0a --- /dev/null +++ b/packages/runtime-vapor/src/apiCreateSlots.ts @@ -0,0 +1,65 @@ +// NOTE: this filed is based on `runtime-core/src/helpers/createSlots.ts` + +import { EMPTY_ARR, isArray } from '@vue/shared' +import { renderWatch } from './renderWatch' +import type { InternalSlots, Slot } from './componentSlots' + +// TODO: SSR + +interface CompiledSlotDescriptor { + name: string + fn: Slot + key?: string +} + +export const createSlots = ( + slots: InternalSlots, + dynamicSlotsGetter?: () => ( + | CompiledSlotDescriptor + | CompiledSlotDescriptor[] + | undefined + )[], +): InternalSlots => { + const dynamicSlotKeys: Record<string, true> = {} + renderWatch( + () => dynamicSlotsGetter?.() ?? EMPTY_ARR, + dynamicSlots => { + for (let i = 0; i < dynamicSlots.length; i++) { + const slot = dynamicSlots[i] + // array of dynamic slot generated by <template v-for="..." #[...]> + if (isArray(slot)) { + for (let j = 0; j < slot.length; j++) { + slots[slot[j].name] = slot[j].fn + dynamicSlotKeys[slot[j].name] = true + } + } else if (slot) { + // conditional single slot generated by <template v-if="..." #foo> + slots[slot.name] = slot.key + ? (...args: any[]) => { + const res = slot.fn(...args) + // attach branch key so each conditional branch is considered a + // different fragment + if (res) (res as any).key = slot.key + return res + } + : slot.fn + dynamicSlotKeys[slot.name] = true + } + } + + // delete stale slots + for (const key in dynamicSlotKeys) { + if ( + // TODO: type (renderWatch) + !dynamicSlots.some((slot: any) => + isArray(slot) ? slot.some(s => s.name === key) : slot?.name === key, + ) + ) { + delete slots[key] + } + } + }, + { immediate: true }, + ) + return slots +} diff --git a/packages/runtime-vapor/src/componentSlots.ts b/packages/runtime-vapor/src/componentSlots.ts index 5bacab2f0..d5b9f7081 100644 --- a/packages/runtime-vapor/src/componentSlots.ts +++ b/packages/runtime-vapor/src/componentSlots.ts @@ -4,7 +4,7 @@ import type { Block } from './apiRender' export type Slot<T extends any = any> = ( ...args: IfAny<T, any[], [T] | (T extends undefined ? [] : never)> -) => Block[] +) => Block export type InternalSlots = { [name: string]: Slot | undefined @@ -14,11 +14,9 @@ export type Slots = Readonly<InternalSlots> export const initSlots = ( instance: ComponentInternalInstance, - slots: Slots | null, + slots: InternalSlots | null, ) => { if (!slots) slots = {} // TODO: normalize? instance.slots = slots } - -// TODO: $stable ? diff --git a/packages/runtime-vapor/src/index.ts b/packages/runtime-vapor/src/index.ts index d6fc21a37..d71f739e6 100644 --- a/packages/runtime-vapor/src/index.ts +++ b/packages/runtime-vapor/src/index.ts @@ -110,7 +110,7 @@ export { export { createIf } from './apiCreateIf' export { createFor } from './apiCreateFor' export { createComponent } from './apiCreateComponent' -export { createSlots } from './slot' +export { createSlots } from './apiCreateSlots' export { resolveComponent, resolveDirective } from './helpers/resolveAssets' // **Internal** DOM-only runtime directive helpers diff --git a/packages/runtime-vapor/src/slot.ts b/packages/runtime-vapor/src/slot.ts deleted file mode 100644 index 46db4c631..000000000 --- a/packages/runtime-vapor/src/slot.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { Slots } from './componentSlots' - -// TODO: intercept? -export const createSlots = (slots: Slots) => { - return slots -} From a5d99454eae67ef639cc95929dcf604fafcc772e Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sun, 17 Mar 2024 19:45:43 +0900 Subject: [PATCH 10/33] chore: fmt --- packages/runtime-vapor/__tests__/_utils.ts | 1 - packages/runtime-vapor/src/index.ts | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime-vapor/__tests__/_utils.ts b/packages/runtime-vapor/__tests__/_utils.ts index befb3dff3..795b9656d 100644 --- a/packages/runtime-vapor/__tests__/_utils.ts +++ b/packages/runtime-vapor/__tests__/_utils.ts @@ -36,7 +36,6 @@ export function makeRender<Component = ObjectComponent | SetupFn>( ) => { app = createVaporApp(component, props, slots) instance = app.mount(container) - return res() } const res = () => ({ diff --git a/packages/runtime-vapor/src/index.ts b/packages/runtime-vapor/src/index.ts index d71f739e6..14d1a3b9c 100644 --- a/packages/runtime-vapor/src/index.ts +++ b/packages/runtime-vapor/src/index.ts @@ -111,6 +111,7 @@ export { createIf } from './apiCreateIf' export { createFor } from './apiCreateFor' export { createComponent } from './apiCreateComponent' export { createSlots } from './apiCreateSlots' + export { resolveComponent, resolveDirective } from './helpers/resolveAssets' // **Internal** DOM-only runtime directive helpers From 3e0d646a97bdcfcc510e92835b502cf58c661be7 Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sun, 17 Mar 2024 19:50:33 +0900 Subject: [PATCH 11/33] chore: slot playground --- playground/src/slots.js | 47 ++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/playground/src/slots.js b/playground/src/slots.js index 2cd6982b1..2e7a4924c 100644 --- a/playground/src/slots.js +++ b/playground/src/slots.js @@ -13,8 +13,6 @@ import { template, } from '@vue/vapor' -const t0 = template('<div class="parnet-container"></div>') - // <template #mySlot="{ message, changeMessage }"> // <div clas="slotted"> // <h1>{{ message }}</h1> @@ -27,30 +25,31 @@ const t1 = template( const Parent = defineComponent({ vapor: true, - - setup(props) { + setup(_) { return (() => { - const n0 = /** @type {any} */ (t0()) - const s0 = createSlots({ - mySlot: scope => { - const n1 = t1() - const n2 = /** @type {any} */ (children(n1, 0)) - const n3 = /** @type {any} */ (children(n1, 1)) - renderEffect(() => { - setText(n2, scope.message()) - }) - on(n3, 'click', scope.changeMessage) - return [n1] - }, - // e.g. default slot - // default: () => { - // const n1 = t1() - // return [n1] - // } - }) /** @type {any} */ - const n1 = createComponent(Child, {}, s0) - return [n0, n1] + const n0 = createComponent( + Child, + {}, + createSlots({ + mySlot: ({ message, changeMessage }) => { + const n1 = t1() + const n2 = /** @type {any} */ (children(n1, 0)) + const n3 = /** @type {any} */ (children(n1, 1)) + renderEffect(() => { + setText(n2, message()) + }) + on(n3, 'click', changeMessage) + return n1 + }, + // e.g. default slot + // default: () => { + // const n1 = t1() + // return n1 + // } + }), + ) + return n0 })() }, }) From ab91662d7427dcb8fb2689e08f4fd1e1738c2a23 Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sun, 17 Mar 2024 19:53:23 +0900 Subject: [PATCH 12/33] chore: remove dead comments --- playground/src/slots.js | 1 - 1 file changed, 1 deletion(-) diff --git a/playground/src/slots.js b/playground/src/slots.js index 2e7a4924c..fa6673297 100644 --- a/playground/src/slots.js +++ b/playground/src/slots.js @@ -1,4 +1,3 @@ -// @ts-check import { children, createComponent, From 0159af955a44bb7f08283eb6bf404ca5318ff694 Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sun, 17 Mar 2024 20:08:13 +0900 Subject: [PATCH 13/33] chore: remove unnecessary diffs --- packages/runtime-vapor/__tests__/componentSlots.spec.ts | 2 +- packages/runtime-vapor/src/component.ts | 2 +- packages/runtime-vapor/src/index.ts | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/runtime-vapor/__tests__/componentSlots.spec.ts b/packages/runtime-vapor/__tests__/componentSlots.spec.ts index 8484482cf..6108f62ca 100644 --- a/packages/runtime-vapor/__tests__/componentSlots.spec.ts +++ b/packages/runtime-vapor/__tests__/componentSlots.spec.ts @@ -2,6 +2,7 @@ import { createComponent, + createSlots, createVaporApp, defineComponent, getCurrentInstance, @@ -9,7 +10,6 @@ import { ref, template, } from '../src' -import { createSlots } from '../src/apiCreateSlots' import { makeRender } from './_utils' const define = makeRender<any>() diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 54c755754..91ff6c0d9 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -19,6 +19,7 @@ import { } from './componentEmits' import { type InternalSlots, type Slots, initSlots } from './componentSlots' import { VaporLifecycleHooks } from './apiLifecycle' + import type { Data } from '@vue/shared' export type Component = FunctionalComponent | ObjectComponent @@ -166,7 +167,6 @@ export function createComponentInstance( // state setupState: EMPTY_OBJ, - props: EMPTY_OBJ, emit: null!, emitted: null, diff --git a/packages/runtime-vapor/src/index.ts b/packages/runtime-vapor/src/index.ts index 14d1a3b9c..15c2e16c2 100644 --- a/packages/runtime-vapor/src/index.ts +++ b/packages/runtime-vapor/src/index.ts @@ -100,7 +100,6 @@ export { onErrorCaptured, // onServerPrefetch, } from './apiLifecycle' - export { createVaporApp, type App, From 1e00d6c0f0a617bffb2a27632eb6b90bdb2663d4 Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sun, 17 Mar 2024 20:13:39 +0900 Subject: [PATCH 14/33] chore: add comments --- README.md | 1 + packages/compiler-vapor/src/generators/component.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index 0937d7dfd..93f6ca0ff 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ The code provided here is a duplicate from `runtime-core` as Vapor cannot import - packages/runtime-vapor/src/component.ts - packages/runtime-vapor/src/componentEmits.ts - packages/runtime-vapor/src/componentProps.ts +- packages/runtime-vapor/src/componentSlots.ts - packages/runtime-vapor/src/enums.ts - packages/runtime-vapor/src/errorHandling.ts - packages/runtime-vapor/src/scheduler.ts diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index ad54d884f..d36c9e842 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -12,6 +12,7 @@ import { import { genExpression } from './expression' import { genPropKey } from './prop' +// TODO: generate component slots export function genCreateComponent( oper: CreateComponentIRNode, context: CodegenContext, From 560964439ea62c1c2c36fed3f5866cb15c47f329 Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Mon, 18 Mar 2024 23:19:58 +0900 Subject: [PATCH 15/33] chore: renderWatch -> renderEffect --- packages/runtime-vapor/src/apiCreateSlots.ts | 74 +++++++++----------- 1 file changed, 35 insertions(+), 39 deletions(-) diff --git a/packages/runtime-vapor/src/apiCreateSlots.ts b/packages/runtime-vapor/src/apiCreateSlots.ts index af619da0a..b9b439306 100644 --- a/packages/runtime-vapor/src/apiCreateSlots.ts +++ b/packages/runtime-vapor/src/apiCreateSlots.ts @@ -1,8 +1,8 @@ // NOTE: this filed is based on `runtime-core/src/helpers/createSlots.ts` -import { EMPTY_ARR, isArray } from '@vue/shared' -import { renderWatch } from './renderWatch' +import { isArray } from '@vue/shared' import type { InternalSlots, Slot } from './componentSlots' +import { renderEffect } from './renderEffect' // TODO: SSR @@ -21,45 +21,41 @@ export const createSlots = ( )[], ): InternalSlots => { const dynamicSlotKeys: Record<string, true> = {} - renderWatch( - () => dynamicSlotsGetter?.() ?? EMPTY_ARR, - dynamicSlots => { - for (let i = 0; i < dynamicSlots.length; i++) { - const slot = dynamicSlots[i] - // array of dynamic slot generated by <template v-for="..." #[...]> - if (isArray(slot)) { - for (let j = 0; j < slot.length; j++) { - slots[slot[j].name] = slot[j].fn - dynamicSlotKeys[slot[j].name] = true - } - } else if (slot) { - // conditional single slot generated by <template v-if="..." #foo> - slots[slot.name] = slot.key - ? (...args: any[]) => { - const res = slot.fn(...args) - // attach branch key so each conditional branch is considered a - // different fragment - if (res) (res as any).key = slot.key - return res - } - : slot.fn - dynamicSlotKeys[slot.name] = true + renderEffect(() => { + const dynamicSlots = dynamicSlotsGetter?.() ?? [] + for (let i = 0; i < dynamicSlots.length; i++) { + const slot = dynamicSlots[i] + // array of dynamic slot generated by <template v-for="..." #[...]> + if (isArray(slot)) { + for (let j = 0; j < slot.length; j++) { + slots[slot[j].name] = slot[j].fn + dynamicSlotKeys[slot[j].name] = true } + } else if (slot) { + // conditional single slot generated by <template v-if="..." #foo> + slots[slot.name] = slot.key + ? (...args: any[]) => { + const res = slot.fn(...args) + // attach branch key so each conditional branch is considered a + // different fragment + if (res) (res as any).key = slot.key + return res + } + : slot.fn + dynamicSlotKeys[slot.name] = true } - - // delete stale slots - for (const key in dynamicSlotKeys) { - if ( - // TODO: type (renderWatch) - !dynamicSlots.some((slot: any) => - isArray(slot) ? slot.some(s => s.name === key) : slot?.name === key, - ) - ) { - delete slots[key] - } + } + // delete stale slots + for (const key in dynamicSlotKeys) { + if ( + // TODO: type (renderWatch) + !dynamicSlots.some((slot: any) => + isArray(slot) ? slot.some(s => s.name === key) : slot?.name === key, + ) + ) { + delete slots[key] } - }, - { immediate: true }, - ) + } + }) return slots } From 6a7957dc48a6c8cbc630ad38280c60885e746fef Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Wed, 20 Mar 2024 02:46:35 +0900 Subject: [PATCH 16/33] chore(runtime-vapor): remove slot arg of createVaporApp --- packages/runtime-vapor/__tests__/_utils.ts | 4 +--- .../__tests__/componentSlots.spec.ts | 22 +++++++++++++------ .../runtime-vapor/src/apiCreateVaporApp.ts | 4 +--- packages/runtime-vapor/src/component.ts | 2 +- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/packages/runtime-vapor/__tests__/_utils.ts b/packages/runtime-vapor/__tests__/_utils.ts index 795b9656d..d9169061d 100644 --- a/packages/runtime-vapor/__tests__/_utils.ts +++ b/packages/runtime-vapor/__tests__/_utils.ts @@ -6,7 +6,6 @@ import { createVaporApp, defineComponent, } from '../src' -import type { Slots } from '../src/componentSlots' import type { RawProps } from '../src/componentProps' export function makeRender<Component = ObjectComponent | SetupFn>( @@ -31,10 +30,9 @@ export function makeRender<Component = ObjectComponent | SetupFn>( let app: App const render = ( props: RawProps = {}, - slots: Slots = {}, container: string | ParentNode = '#host', ) => { - app = createVaporApp(component, props, slots) + app = createVaporApp(component, props) instance = app.mount(container) return res() } diff --git a/packages/runtime-vapor/__tests__/componentSlots.spec.ts b/packages/runtime-vapor/__tests__/componentSlots.spec.ts index 6108f62ca..ea741c5e3 100644 --- a/packages/runtime-vapor/__tests__/componentSlots.spec.ts +++ b/packages/runtime-vapor/__tests__/componentSlots.spec.ts @@ -58,7 +58,7 @@ describe('component: slots', () => { test('initSlots: instance.slots should be set correctly', () => { let instance: any - const { render } = define({ + const Comp = defineComponent({ render() { const t0 = template('<div></div>') const n0 = t0() @@ -67,12 +67,20 @@ describe('component: slots', () => { }, }) - render( - {}, - createSlots({ - header: () => template('header')(), - }), - ) + const { render } = define({ + render() { + return createComponent( + Comp, + {}, + createSlots({ + header: () => template('header')(), + }), + ) + }, + }) + + render() + expect(instance.slots.header()).toMatchObject( document.createTextNode('header'), ) diff --git a/packages/runtime-vapor/src/apiCreateVaporApp.ts b/packages/runtime-vapor/src/apiCreateVaporApp.ts index 084514fb6..af8e480fd 100644 --- a/packages/runtime-vapor/src/apiCreateVaporApp.ts +++ b/packages/runtime-vapor/src/apiCreateVaporApp.ts @@ -8,12 +8,10 @@ import { warn } from './warning' import { version } from '.' import { render, setupComponent, unmountComponent } from './apiRender' import type { RawProps } from './componentProps' -import type { Slots } from './componentSlots' export function createVaporApp( rootComponent: Component, rootProps: RawProps | null = null, - rootSlots: Slots | null = null, ): App { if (rootProps != null && !isObject(rootProps)) { __DEV__ && warn(`root props passed to app.mount() must be an object.`) @@ -40,7 +38,7 @@ export function createVaporApp( mount(rootContainer): any { if (!instance) { - instance = createComponentInstance(rootComponent, rootProps, rootSlots) + instance = createComponentInstance(rootComponent, rootProps) setupComponent(instance) render(instance, rootContainer) return instance diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 11f9687dc..6eb5caf2d 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -147,7 +147,7 @@ let uid = 0 export function createComponentInstance( component: ObjectComponent | FunctionalComponent, rawProps: RawProps | null, - slots: Slots | null, + slots: Slots | null = null, ): ComponentInternalInstance { const instance: ComponentInternalInstance = { [componentKey]: true, From d12c2acc9f62056b0f1244e91b9679f29b878d64 Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Wed, 20 Mar 2024 02:53:27 +0900 Subject: [PATCH 17/33] chore(runtime-vapor): provide slots to setup context --- packages/runtime-vapor/src/apiRender.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/runtime-vapor/src/apiRender.ts b/packages/runtime-vapor/src/apiRender.ts index 3ddaeb5cc..e0354ee8e 100644 --- a/packages/runtime-vapor/src/apiRender.ts +++ b/packages/runtime-vapor/src/apiRender.ts @@ -26,8 +26,8 @@ export function setupComponent( ): void { const reset = setCurrentInstance(instance) instance.scope.run(() => { - const { component, props, emit, attrs } = instance - const ctx = { expose: () => {}, emit, attrs } + const { component, props, emit, attrs, slots } = instance + const ctx = { expose: () => {}, emit, attrs, slots } const setupFn = isFunction(component) ? component : component.setup const stateOrNode = setupFn && setupFn(props, ctx) From 9dfc966fce7588a540cbc319a3443e4edd81ec0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= <sxzz@sxzz.moe> Date: Sat, 23 Mar 2024 01:41:36 +0800 Subject: [PATCH 18/33] refactor: tidy --- playground/src/slots.js | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/playground/src/slots.js b/playground/src/slots.js index fa6673297..35116ffc2 100644 --- a/playground/src/slots.js +++ b/playground/src/slots.js @@ -1,9 +1,9 @@ +// @ts-check import { children, createComponent, createSlots, defineComponent, - getCurrentInstance, insert, on, ref, @@ -24,7 +24,7 @@ const t1 = template( const Parent = defineComponent({ vapor: true, - setup(_) { + setup() { return (() => { /** @type {any} */ const n0 = createComponent( @@ -35,9 +35,7 @@ const Parent = defineComponent({ const n1 = t1() const n2 = /** @type {any} */ (children(n1, 0)) const n3 = /** @type {any} */ (children(n1, 1)) - renderEffect(() => { - setText(n2, message()) - }) + renderEffect(() => setText(n2, message())) on(n3, 'click', changeMessage) return n1 }, @@ -59,16 +57,13 @@ const t2 = template( const Child = defineComponent({ vapor: true, - setup(_, { expose: __expose }) { - __expose() + setup(_, { slots }) { const message = ref('Hello World!') function changeMessage() { message.value += '!' } return (() => { - const instance = /** @type {any} */ (getCurrentInstance()) - const { slots } = instance // <div> // <slot name="mySlot" :message="msg" :changeMessage="changeMessage" /> // <button @click="changeMessage">button in child</button> @@ -76,10 +71,12 @@ const Child = defineComponent({ const n0 = /** @type {any} */ (t2()) const n1 = /** @type {any} */ (children(n0, 0)) on(n1, 'click', () => changeMessage) - const s0 = slots.mySlot({ - message: () => message.value, - changeMessage: () => changeMessage, - }) + const s0 = /** @type {any} */ ( + slots.mySlot?.({ + message: () => message.value, + changeMessage: () => changeMessage, + }) + ) insert(s0, n0, n1) return n0 })() From 9c122eb4849c94ad768a95899aa82406bc7a2a20 Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sat, 23 Mar 2024 13:01:02 +0900 Subject: [PATCH 19/33] refactor(runtime-vapor): CompiledSlotDescriptor -> DynamicSlot --- packages/runtime-vapor/src/apiCreateSlots.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/runtime-vapor/src/apiCreateSlots.ts b/packages/runtime-vapor/src/apiCreateSlots.ts index b9b439306..90f2613c2 100644 --- a/packages/runtime-vapor/src/apiCreateSlots.ts +++ b/packages/runtime-vapor/src/apiCreateSlots.ts @@ -6,7 +6,7 @@ import { renderEffect } from './renderEffect' // TODO: SSR -interface CompiledSlotDescriptor { +interface DynamicSlot { name: string fn: Slot key?: string @@ -14,11 +14,7 @@ interface CompiledSlotDescriptor { export const createSlots = ( slots: InternalSlots, - dynamicSlotsGetter?: () => ( - | CompiledSlotDescriptor - | CompiledSlotDescriptor[] - | undefined - )[], + dynamicSlotsGetter?: () => (DynamicSlot | DynamicSlot[] | undefined)[], ): InternalSlots => { const dynamicSlotKeys: Record<string, true> = {} renderEffect(() => { From 9479dbaad4a4132b88fb3d846a5c35d5ce72ecde Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sat, 23 Mar 2024 13:05:26 +0900 Subject: [PATCH 20/33] refactor(runtime-vapor): renderEffect -> baseWatch --- packages/runtime-vapor/src/apiCreateSlots.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/runtime-vapor/src/apiCreateSlots.ts b/packages/runtime-vapor/src/apiCreateSlots.ts index 90f2613c2..a67505a78 100644 --- a/packages/runtime-vapor/src/apiCreateSlots.ts +++ b/packages/runtime-vapor/src/apiCreateSlots.ts @@ -1,8 +1,8 @@ // NOTE: this filed is based on `runtime-core/src/helpers/createSlots.ts` import { isArray } from '@vue/shared' +import { baseWatch } from '@vue/reactivity' import type { InternalSlots, Slot } from './componentSlots' -import { renderEffect } from './renderEffect' // TODO: SSR @@ -17,7 +17,7 @@ export const createSlots = ( dynamicSlotsGetter?: () => (DynamicSlot | DynamicSlot[] | undefined)[], ): InternalSlots => { const dynamicSlotKeys: Record<string, true> = {} - renderEffect(() => { + baseWatch(() => { const dynamicSlots = dynamicSlotsGetter?.() ?? [] for (let i = 0; i < dynamicSlots.length; i++) { const slot = dynamicSlots[i] @@ -44,8 +44,7 @@ export const createSlots = ( // delete stale slots for (const key in dynamicSlotKeys) { if ( - // TODO: type (renderWatch) - !dynamicSlots.some((slot: any) => + !dynamicSlots.some(slot => isArray(slot) ? slot.some(s => s.name === key) : slot?.name === key, ) ) { From a54617c6fc0a96f7f3bf4e4d6c05f6e583cf6d51 Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sat, 23 Mar 2024 13:06:04 +0900 Subject: [PATCH 21/33] refactor(runtime-vapor): no export createSlots --- packages/runtime-vapor/src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/runtime-vapor/src/index.ts b/packages/runtime-vapor/src/index.ts index eaba6f7f2..cb8b28daf 100644 --- a/packages/runtime-vapor/src/index.ts +++ b/packages/runtime-vapor/src/index.ts @@ -119,7 +119,6 @@ export { export { createIf } from './apiCreateIf' export { createFor } from './apiCreateFor' export { createComponent } from './apiCreateComponent' -export { createSlots } from './apiCreateSlots' export { resolveComponent, resolveDirective } from './helpers/resolveAssets' From 082f6a1eef5739abf43159b6a9f5118a18ebbbef Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sat, 23 Mar 2024 13:46:48 +0900 Subject: [PATCH 22/33] refactor(runtime-vapor): create component slots --- .../__tests__/componentAttrs.spec.ts | 4 ++ .../__tests__/componentProps.spec.ts | 1 + .../__tests__/componentSlots.spec.ts | 52 +++++----------- .../runtime-vapor/src/apiCreateComponent.ts | 4 +- packages/runtime-vapor/src/apiCreateSlots.ts | 56 ----------------- .../runtime-vapor/src/apiCreateVaporApp.ts | 1 + packages/runtime-vapor/src/component.ts | 10 +++- packages/runtime-vapor/src/componentSlots.ts | 60 ++++++++++++++++++- playground/src/slots.js | 5 +- 9 files changed, 92 insertions(+), 101 deletions(-) delete mode 100644 packages/runtime-vapor/src/apiCreateSlots.ts diff --git a/packages/runtime-vapor/__tests__/componentAttrs.spec.ts b/packages/runtime-vapor/__tests__/componentAttrs.spec.ts index 42a0a2995..46a472b03 100644 --- a/packages/runtime-vapor/__tests__/componentAttrs.spec.ts +++ b/packages/runtime-vapor/__tests__/componentAttrs.spec.ts @@ -41,6 +41,7 @@ describe('attribute fallthrough', () => { }, ], null, + null, true, ) }, @@ -87,6 +88,7 @@ describe('attribute fallthrough', () => { }, ], null, + null, true, ) }, @@ -126,6 +128,7 @@ describe('attribute fallthrough', () => { }, ], null, + null, true, ) return n0 @@ -148,6 +151,7 @@ describe('attribute fallthrough', () => { }, ], null, + null, true, ) }, diff --git a/packages/runtime-vapor/__tests__/componentProps.spec.ts b/packages/runtime-vapor/__tests__/componentProps.spec.ts index 10a21f8f9..4487c65b1 100644 --- a/packages/runtime-vapor/__tests__/componentProps.spec.ts +++ b/packages/runtime-vapor/__tests__/componentProps.spec.ts @@ -245,6 +245,7 @@ describe('component: props', () => { id: () => _ctx.id, }, null, + null, true, ) }, diff --git a/packages/runtime-vapor/__tests__/componentSlots.spec.ts b/packages/runtime-vapor/__tests__/componentSlots.spec.ts index ea741c5e3..fa3852de0 100644 --- a/packages/runtime-vapor/__tests__/componentSlots.spec.ts +++ b/packages/runtime-vapor/__tests__/componentSlots.spec.ts @@ -2,7 +2,6 @@ import { createComponent, - createSlots, createVaporApp, defineComponent, getCurrentInstance, @@ -69,13 +68,7 @@ describe('component: slots', () => { const { render } = define({ render() { - return createComponent( - Comp, - {}, - createSlots({ - header: () => template('header')(), - }), - ) + return createComponent(Comp, {}, { header: () => template('header')() }) }, }) @@ -88,12 +81,9 @@ describe('component: slots', () => { // TODO: test case name test('initSlots: instance.slots should be set correctly (when vnode.shapeFlag is not SLOTS_CHILDREN)', () => { - const { slots } = renderWithSlots( - createSlots({ - // TODO: normalize from array? - default: () => template('<span></span>')(), - }), - ) + const { slots } = renderWithSlots({ + default: () => template('<span></span>')(), + }) // expect( // '[Vue warn]: Non-function value encountered for default slot. Prefer function slots for better performance.', @@ -113,15 +103,11 @@ describe('component: slots', () => { const { render } = define({ render() { - return createComponent( - Child, - {}, - createSlots({ _: 2 as any }, () => [ - flag1.value - ? { name: 'one', fn: () => template('<span></span>')() } - : { name: 'two', fn: () => template('<div></div>')() }, - ]), - ) + return createComponent(Child, {}, { _: 2 as any }, () => [ + flag1.value + ? { name: 'one', fn: () => template('<span></span>')() } + : { name: 'two', fn: () => template('<div></div>')() }, + ]) }, }) @@ -164,7 +150,7 @@ describe('component: slots', () => { Child, {}, // TODO: maybe it is not supported - createSlots(flag1.value ? oldSlots : newSlots), + flag1.value ? oldSlots : newSlots, ) })() }, @@ -193,15 +179,11 @@ describe('component: slots', () => { const { render } = define({ setup() { - return createComponent( - Child, - {}, - createSlots({}, () => [ - flag1.value - ? [{ name: 'header', fn: () => template('header')() }] - : [{ name: 'footer', fn: () => template('footer')() }], - ]), - ) + return createComponent(Child, {}, {}, () => [ + flag1.value + ? [{ name: 'header', fn: () => template('header')() }] + : [{ name: 'footer', fn: () => template('footer')() }], + ]) }, }) render() @@ -235,9 +217,7 @@ describe('component: slots', () => { return createComponent( Comp, {}, - createSlots({ - default: () => template('msg')(), - }), + { default: () => template('msg')() }, )! }, }) diff --git a/packages/runtime-vapor/src/apiCreateComponent.ts b/packages/runtime-vapor/src/apiCreateComponent.ts index 3a41f6329..dc693c0a1 100644 --- a/packages/runtime-vapor/src/apiCreateComponent.ts +++ b/packages/runtime-vapor/src/apiCreateComponent.ts @@ -5,13 +5,14 @@ import { } from './component' import { setupComponent } from './apiRender' import type { RawProps } from './componentProps' -import type { Slots } from './componentSlots' +import type { DinamicSlotsGetter, Slots } from './componentSlots' import { withAttrs } from './componentAttrs' export function createComponent( comp: Component, rawProps: RawProps | null = null, slots: Slots | null = null, + dynamicSlotsGetter: DinamicSlotsGetter | null = null, singleRoot: boolean = false, ) { const current = currentInstance! @@ -19,6 +20,7 @@ export function createComponent( comp, singleRoot ? withAttrs(rawProps) : rawProps, slots, + dynamicSlotsGetter, ) setupComponent(instance, singleRoot) diff --git a/packages/runtime-vapor/src/apiCreateSlots.ts b/packages/runtime-vapor/src/apiCreateSlots.ts deleted file mode 100644 index a67505a78..000000000 --- a/packages/runtime-vapor/src/apiCreateSlots.ts +++ /dev/null @@ -1,56 +0,0 @@ -// NOTE: this filed is based on `runtime-core/src/helpers/createSlots.ts` - -import { isArray } from '@vue/shared' -import { baseWatch } from '@vue/reactivity' -import type { InternalSlots, Slot } from './componentSlots' - -// TODO: SSR - -interface DynamicSlot { - name: string - fn: Slot - key?: string -} - -export const createSlots = ( - slots: InternalSlots, - dynamicSlotsGetter?: () => (DynamicSlot | DynamicSlot[] | undefined)[], -): InternalSlots => { - const dynamicSlotKeys: Record<string, true> = {} - baseWatch(() => { - const dynamicSlots = dynamicSlotsGetter?.() ?? [] - for (let i = 0; i < dynamicSlots.length; i++) { - const slot = dynamicSlots[i] - // array of dynamic slot generated by <template v-for="..." #[...]> - if (isArray(slot)) { - for (let j = 0; j < slot.length; j++) { - slots[slot[j].name] = slot[j].fn - dynamicSlotKeys[slot[j].name] = true - } - } else if (slot) { - // conditional single slot generated by <template v-if="..." #foo> - slots[slot.name] = slot.key - ? (...args: any[]) => { - const res = slot.fn(...args) - // attach branch key so each conditional branch is considered a - // different fragment - if (res) (res as any).key = slot.key - return res - } - : slot.fn - dynamicSlotKeys[slot.name] = true - } - } - // delete stale slots - for (const key in dynamicSlotKeys) { - if ( - !dynamicSlots.some(slot => - isArray(slot) ? slot.some(s => s.name === key) : slot?.name === key, - ) - ) { - delete slots[key] - } - } - }) - return slots -} diff --git a/packages/runtime-vapor/src/apiCreateVaporApp.ts b/packages/runtime-vapor/src/apiCreateVaporApp.ts index 7356ba006..2d07bbba3 100644 --- a/packages/runtime-vapor/src/apiCreateVaporApp.ts +++ b/packages/runtime-vapor/src/apiCreateVaporApp.ts @@ -45,6 +45,7 @@ export function createVaporApp( rootComponent, rootProps, null, + null, context, ) setupComponent(instance) diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index c3e996c06..18c090ba9 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -17,7 +17,12 @@ import { emit, normalizeEmitsOptions, } from './componentEmits' -import { type InternalSlots, type Slots, initSlots } from './componentSlots' +import { + type DinamicSlotsGetter, + type InternalSlots, + type Slots, + initSlots, +} from './componentSlots' import { VaporLifecycleHooks } from './apiLifecycle' import { warn } from './warning' import { type AppContext, createAppContext } from './apiCreateVaporApp' @@ -196,6 +201,7 @@ export function createComponentInstance( component: ObjectComponent | FunctionalComponent, rawProps: RawProps | null, slots: Slots | null = null, + dynamicSlotsGetter: DinamicSlotsGetter | null = null, // application root node only appContext: AppContext | null = null, ): ComponentInternalInstance { @@ -293,7 +299,7 @@ export function createComponentInstance( // [VaporLifecycleHooks.SERVER_PREFETCH]: null, } initProps(instance, rawProps, !isFunction(component)) - initSlots(instance, slots) + initSlots(instance, slots, dynamicSlotsGetter) instance.emit = emit.bind(null, instance) return instance diff --git a/packages/runtime-vapor/src/componentSlots.ts b/packages/runtime-vapor/src/componentSlots.ts index d5b9f7081..e74440bfc 100644 --- a/packages/runtime-vapor/src/componentSlots.ts +++ b/packages/runtime-vapor/src/componentSlots.ts @@ -1,7 +1,10 @@ -import type { IfAny } from '@vue/shared' +import { type IfAny, isArray } from '@vue/shared' +import { baseWatch } from '@vue/reactivity' import type { ComponentInternalInstance } from './component' import type { Block } from './apiRender' +// TODO: SSR + export type Slot<T extends any = any> = ( ...args: IfAny<T, any[], [T] | (T extends undefined ? [] : never)> ) => Block @@ -12,11 +15,62 @@ export type InternalSlots = { export type Slots = Readonly<InternalSlots> +export interface DynamicSlot { + name: string + fn: Slot + key?: string +} + +export type DinamicSlotsGetter = () => (DynamicSlot | DynamicSlot[])[] + export const initSlots = ( instance: ComponentInternalInstance, slots: InternalSlots | null, + dynamicSlotsGetter: DinamicSlotsGetter | null = null, ) => { if (!slots) slots = {} - // TODO: normalize? - instance.slots = slots + instance.slots = createSlots(slots, dynamicSlotsGetter) +} + +const createSlots = ( + slots: InternalSlots, + dynamicSlotsGetter: DinamicSlotsGetter | null = null, +): InternalSlots => { + const dynamicSlotKeys: Record<string, true> = {} + baseWatch(() => { + const dynamicSlots = dynamicSlotsGetter?.() ?? [] + for (let i = 0; i < dynamicSlots.length; i++) { + const slot = dynamicSlots[i] + // array of dynamic slot generated by <template v-for="..." #[...]> + if (isArray(slot)) { + for (let j = 0; j < slot.length; j++) { + slots[slot[j].name] = slot[j].fn + dynamicSlotKeys[slot[j].name] = true + } + } else if (slot) { + // conditional single slot generated by <template v-if="..." #foo> + slots[slot.name] = slot.key + ? (...args: any[]) => { + const res = slot.fn(...args) + // attach branch key so each conditional branch is considered a + // different fragment + if (res) (res as any).key = slot.key + return res + } + : slot.fn + dynamicSlotKeys[slot.name] = true + } + } + // delete stale slots + for (const key in dynamicSlotKeys) { + if ( + !dynamicSlots.some(slot => + isArray(slot) ? slot.some(s => s.name === key) : slot?.name === key, + ) + ) { + delete slots[key] + } + } + }) + return slots } diff --git a/playground/src/slots.js b/playground/src/slots.js index fa6673297..ede7b7081 100644 --- a/playground/src/slots.js +++ b/playground/src/slots.js @@ -1,7 +1,6 @@ import { children, createComponent, - createSlots, defineComponent, getCurrentInstance, insert, @@ -30,7 +29,7 @@ const Parent = defineComponent({ const n0 = createComponent( Child, {}, - createSlots({ + { mySlot: ({ message, changeMessage }) => { const n1 = t1() const n2 = /** @type {any} */ (children(n1, 0)) @@ -46,7 +45,7 @@ const Parent = defineComponent({ // const n1 = t1() // return n1 // } - }), + }, ) return n0 })() From cf18747ddce7e661b20c0943c273ed358fe8e35f Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sat, 23 Mar 2024 13:58:04 +0900 Subject: [PATCH 23/33] chore(runtime-vapor): component slots tests --- .../__tests__/componentSlots.spec.ts | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/packages/runtime-vapor/__tests__/componentSlots.spec.ts b/packages/runtime-vapor/__tests__/componentSlots.spec.ts index fa3852de0..37584a549 100644 --- a/packages/runtime-vapor/__tests__/componentSlots.spec.ts +++ b/packages/runtime-vapor/__tests__/componentSlots.spec.ts @@ -41,19 +41,15 @@ describe('component: slots', () => { expect(slots).toMatchObject({ _: 1 }) }) - test.todo( - 'initSlots: should normalize object slots (when value is null, string, array)', - () => { - // TODO: normalize - }, - ) - - test.todo( - 'initSlots: should normalize object slots (when value is function)', - () => { - // TODO: normalize - }, - ) + // NOTE: slot normalization is not supported + // test.todo( + // 'initSlots: should normalize object slots (when value is null, string, array)', + // () => {}, + // ) + // test.todo( + // 'initSlots: should normalize object slots (when value is function)', + // () => {}, + // ) test('initSlots: instance.slots should be set correctly', () => { let instance: any @@ -79,8 +75,8 @@ describe('component: slots', () => { ) }) - // TODO: test case name - test('initSlots: instance.slots should be set correctly (when vnode.shapeFlag is not SLOTS_CHILDREN)', () => { + // runtime-core's "initSlots: instance.slots should be set correctly (when vnode.shapeFlag is not SLOTS_CHILDREN)" + test('initSlots: instance.slots should be set correctly', () => { const { slots } = renderWithSlots({ default: () => template('<span></span>')(), }) @@ -167,8 +163,8 @@ describe('component: slots', () => { }, ) - // TODO: test case name - test('updateSlots: instance.slots should be update correctly (when vnode.shapeFlag is not SLOTS_CHILDREN)', async () => { + // runtime-core's "updateSlots: instance.slots should be update correctly (when vnode.shapeFlag is not SLOTS_CHILDREN)" + test('updateSlots: instance.slots should be update correctly', async () => { const flag1 = ref(true) let instance: any From 38af9aac1545b8ff484b47a5ea5c3560e913d8a5 Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sat, 23 Mar 2024 14:04:03 +0900 Subject: [PATCH 24/33] feat(runtime-vapor): set dynamic slots updation scheduler --- packages/runtime-vapor/src/componentSlots.ts | 72 +++++++++++--------- 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/packages/runtime-vapor/src/componentSlots.ts b/packages/runtime-vapor/src/componentSlots.ts index e74440bfc..dd1c772d8 100644 --- a/packages/runtime-vapor/src/componentSlots.ts +++ b/packages/runtime-vapor/src/componentSlots.ts @@ -2,6 +2,7 @@ import { type IfAny, isArray } from '@vue/shared' import { baseWatch } from '@vue/reactivity' import type { ComponentInternalInstance } from './component' import type { Block } from './apiRender' +import { createVaporPreScheduler } from './scheduler' // TODO: SSR @@ -29,48 +30,53 @@ export const initSlots = ( dynamicSlotsGetter: DinamicSlotsGetter | null = null, ) => { if (!slots) slots = {} - instance.slots = createSlots(slots, dynamicSlotsGetter) + instance.slots = createSlots(instance, slots, dynamicSlotsGetter) } const createSlots = ( + instance: ComponentInternalInstance, slots: InternalSlots, dynamicSlotsGetter: DinamicSlotsGetter | null = null, ): InternalSlots => { const dynamicSlotKeys: Record<string, true> = {} - baseWatch(() => { - const dynamicSlots = dynamicSlotsGetter?.() ?? [] - for (let i = 0; i < dynamicSlots.length; i++) { - const slot = dynamicSlots[i] - // array of dynamic slot generated by <template v-for="..." #[...]> - if (isArray(slot)) { - for (let j = 0; j < slot.length; j++) { - slots[slot[j].name] = slot[j].fn - dynamicSlotKeys[slot[j].name] = true + baseWatch( + () => { + const dynamicSlots = dynamicSlotsGetter?.() ?? [] + for (let i = 0; i < dynamicSlots.length; i++) { + const slot = dynamicSlots[i] + // array of dynamic slot generated by <template v-for="..." #[...]> + if (isArray(slot)) { + for (let j = 0; j < slot.length; j++) { + slots[slot[j].name] = slot[j].fn + dynamicSlotKeys[slot[j].name] = true + } + } else if (slot) { + // conditional single slot generated by <template v-if="..." #foo> + slots[slot.name] = slot.key + ? (...args: any[]) => { + const res = slot.fn(...args) + // attach branch key so each conditional branch is considered a + // different fragment + if (res) (res as any).key = slot.key + return res + } + : slot.fn + dynamicSlotKeys[slot.name] = true } - } else if (slot) { - // conditional single slot generated by <template v-if="..." #foo> - slots[slot.name] = slot.key - ? (...args: any[]) => { - const res = slot.fn(...args) - // attach branch key so each conditional branch is considered a - // different fragment - if (res) (res as any).key = slot.key - return res - } - : slot.fn - dynamicSlotKeys[slot.name] = true } - } - // delete stale slots - for (const key in dynamicSlotKeys) { - if ( - !dynamicSlots.some(slot => - isArray(slot) ? slot.some(s => s.name === key) : slot?.name === key, - ) - ) { - delete slots[key] + // delete stale slots + for (const key in dynamicSlotKeys) { + if ( + !dynamicSlots.some(slot => + isArray(slot) ? slot.some(s => s.name === key) : slot?.name === key, + ) + ) { + delete slots[key] + } } - } - }) + }, + undefined, + { scheduler: createVaporPreScheduler(instance) }, + ) return slots } From 13cc5978ac82eb921783128edd965771c9ca9ef5 Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sat, 23 Mar 2024 20:30:02 +0900 Subject: [PATCH 25/33] refactor(runtime-vapor): dynamic slots --- .../runtime-vapor/src/apiCreateComponent.ts | 6 +- packages/runtime-vapor/src/component.ts | 6 +- packages/runtime-vapor/src/componentSlots.ts | 102 +++++++++--------- 3 files changed, 59 insertions(+), 55 deletions(-) diff --git a/packages/runtime-vapor/src/apiCreateComponent.ts b/packages/runtime-vapor/src/apiCreateComponent.ts index dc693c0a1..fc6e62e17 100644 --- a/packages/runtime-vapor/src/apiCreateComponent.ts +++ b/packages/runtime-vapor/src/apiCreateComponent.ts @@ -5,14 +5,14 @@ import { } from './component' import { setupComponent } from './apiRender' import type { RawProps } from './componentProps' -import type { DinamicSlotsGetter, Slots } from './componentSlots' +import type { DinamicSlots, Slots } from './componentSlots' import { withAttrs } from './componentAttrs' export function createComponent( comp: Component, rawProps: RawProps | null = null, slots: Slots | null = null, - dynamicSlotsGetter: DinamicSlotsGetter | null = null, + dynamicSlots: DinamicSlots | null = null, singleRoot: boolean = false, ) { const current = currentInstance! @@ -20,7 +20,7 @@ export function createComponent( comp, singleRoot ? withAttrs(rawProps) : rawProps, slots, - dynamicSlotsGetter, + dynamicSlots, ) setupComponent(instance, singleRoot) diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 18c090ba9..527eb38c1 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -18,7 +18,7 @@ import { normalizeEmitsOptions, } from './componentEmits' import { - type DinamicSlotsGetter, + type DinamicSlots, type InternalSlots, type Slots, initSlots, @@ -201,7 +201,7 @@ export function createComponentInstance( component: ObjectComponent | FunctionalComponent, rawProps: RawProps | null, slots: Slots | null = null, - dynamicSlotsGetter: DinamicSlotsGetter | null = null, + dynamicSlots: DinamicSlots | null = null, // application root node only appContext: AppContext | null = null, ): ComponentInternalInstance { @@ -299,7 +299,7 @@ export function createComponentInstance( // [VaporLifecycleHooks.SERVER_PREFETCH]: null, } initProps(instance, rawProps, !isFunction(component)) - initSlots(instance, slots, dynamicSlotsGetter) + initSlots(instance, slots, dynamicSlots) instance.emit = emit.bind(null, instance) return instance diff --git a/packages/runtime-vapor/src/componentSlots.ts b/packages/runtime-vapor/src/componentSlots.ts index dd1c772d8..6c06aaf20 100644 --- a/packages/runtime-vapor/src/componentSlots.ts +++ b/packages/runtime-vapor/src/componentSlots.ts @@ -22,61 +22,65 @@ export interface DynamicSlot { key?: string } -export type DinamicSlotsGetter = () => (DynamicSlot | DynamicSlot[])[] +export type DinamicSlots = () => (DynamicSlot | DynamicSlot[])[] export const initSlots = ( instance: ComponentInternalInstance, - slots: InternalSlots | null, - dynamicSlotsGetter: DinamicSlotsGetter | null = null, + rawSlots: InternalSlots | null = null, + dynamicSlots: DinamicSlots | null = null, ) => { - if (!slots) slots = {} - instance.slots = createSlots(instance, slots, dynamicSlotsGetter) -} + const slots: InternalSlots = {} + for (const key in rawSlots) { + Object.defineProperty(slots, key, { + get: () => rawSlots[key], + enumerable: true, + }) + } -const createSlots = ( - instance: ComponentInternalInstance, - slots: InternalSlots, - dynamicSlotsGetter: DinamicSlotsGetter | null = null, -): InternalSlots => { - const dynamicSlotKeys: Record<string, true> = {} - baseWatch( - () => { - const dynamicSlots = dynamicSlotsGetter?.() ?? [] - for (let i = 0; i < dynamicSlots.length; i++) { - const slot = dynamicSlots[i] - // array of dynamic slot generated by <template v-for="..." #[...]> - if (isArray(slot)) { - for (let j = 0; j < slot.length; j++) { - slots[slot[j].name] = slot[j].fn - dynamicSlotKeys[slot[j].name] = true + if (dynamicSlots) { + const dynamicSlotKeys: Record<string, true> = {} + baseWatch( + () => { + const _dynamicSlots = dynamicSlots() + for (let i = 0; i < _dynamicSlots.length; i++) { + const slot = _dynamicSlots[i] + // array of dynamic slot generated by <template v-for="..." #[...]> + if (isArray(slot)) { + for (let j = 0; j < slot.length; j++) { + slots[slot[j].name] = slot[j].fn + dynamicSlotKeys[slot[j].name] = true + } + } else if (slot) { + // conditional single slot generated by <template v-if="..." #foo> + slots[slot.name] = slot.key + ? (...args: any[]) => { + const res = slot.fn(...args) + // attach branch key so each conditional branch is considered a + // different fragment + if (res) (res as any).key = slot.key + return res + } + : slot.fn + dynamicSlotKeys[slot.name] = true } - } else if (slot) { - // conditional single slot generated by <template v-if="..." #foo> - slots[slot.name] = slot.key - ? (...args: any[]) => { - const res = slot.fn(...args) - // attach branch key so each conditional branch is considered a - // different fragment - if (res) (res as any).key = slot.key - return res - } - : slot.fn - dynamicSlotKeys[slot.name] = true } - } - // delete stale slots - for (const key in dynamicSlotKeys) { - if ( - !dynamicSlots.some(slot => - isArray(slot) ? slot.some(s => s.name === key) : slot?.name === key, - ) - ) { - delete slots[key] + // delete stale slots + for (const key in dynamicSlotKeys) { + if ( + !_dynamicSlots.some(slot => + isArray(slot) + ? slot.some(s => s.name === key) + : slot?.name === key, + ) + ) { + delete slots[key] + } } - } - }, - undefined, - { scheduler: createVaporPreScheduler(instance) }, - ) - return slots + }, + undefined, + { scheduler: createVaporPreScheduler(instance) }, + ) + } + + instance.slots = slots } From f1f8b4213b5d88a192afb3b3e1594232c9ed1d52 Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sat, 23 Mar 2024 20:32:25 +0900 Subject: [PATCH 26/33] chore(runtime-vapor): remove dead test --- .../runtime-vapor/__tests__/apiInject.spec.ts | 1 - .../__tests__/componentSlots.spec.ts | 45 +------------------ 2 files changed, 2 insertions(+), 44 deletions(-) diff --git a/packages/runtime-vapor/__tests__/apiInject.spec.ts b/packages/runtime-vapor/__tests__/apiInject.spec.ts index 1b0b35cd3..3414f136c 100644 --- a/packages/runtime-vapor/__tests__/apiInject.spec.ts +++ b/packages/runtime-vapor/__tests__/apiInject.spec.ts @@ -6,7 +6,6 @@ import { createComponent, createTextNode, createVaporApp, - getCurrentInstance, hasInjectionContext, inject, nextTick, diff --git a/packages/runtime-vapor/__tests__/componentSlots.spec.ts b/packages/runtime-vapor/__tests__/componentSlots.spec.ts index 37584a549..d0252f57c 100644 --- a/packages/runtime-vapor/__tests__/componentSlots.spec.ts +++ b/packages/runtime-vapor/__tests__/componentSlots.spec.ts @@ -119,49 +119,8 @@ describe('component: slots', () => { expect(instance.slots).toHaveProperty('two') }) - test.todo( - 'updateSlots: instance.slots should be updated correctly', - async () => { - const flag1 = ref(true) - - let instance: any - const Child = () => { - instance = getCurrentInstance() - return template('child')() - } - - const oldSlots = { - header: () => template('header')(), - footer: undefined, - } - const newSlots = { - header: undefined, - footer: () => template('footer')(), - } - - const { render } = define({ - setup() { - return (() => { - return createComponent( - Child, - {}, - // TODO: maybe it is not supported - flag1.value ? oldSlots : newSlots, - ) - })() - }, - }) - - render() - - expect(instance.slots).toMatchObject({ _: null }) - - flag1.value = false - await nextTick() - - expect(instance.slots).toMatchObject({ _: null }) - }, - ) + // NOTE: it is not supported + // test('updateSlots: instance.slots should be updated correctly (when slotType is null)', () => {}) // runtime-core's "updateSlots: instance.slots should be update correctly (when vnode.shapeFlag is not SLOTS_CHILDREN)" test('updateSlots: instance.slots should be update correctly', async () => { From e04e00aa601d40d8e2f2eff6de87109f0c6c52aa Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sun, 24 Mar 2024 00:16:05 +0900 Subject: [PATCH 27/33] chore: remove tracking $slots --- packages/runtime-vapor/src/component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 527eb38c1..41b8396b0 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -341,7 +341,6 @@ function getSlotsProxy(instance: ComponentInternalInstance): Slots { instance.slotsProxy || (instance.slotsProxy = new Proxy(instance.slots, { get(target, key: string) { - track(instance, TrackOpTypes.GET, '$slots') return target[key] }, })) From f6afe50bbd7f6115be7f0903ef91759d55653f2f Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sun, 24 Mar 2024 00:17:13 +0900 Subject: [PATCH 28/33] refactor: for -> extend --- packages/runtime-vapor/src/componentSlots.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/runtime-vapor/src/componentSlots.ts b/packages/runtime-vapor/src/componentSlots.ts index 6c06aaf20..29b84f4a1 100644 --- a/packages/runtime-vapor/src/componentSlots.ts +++ b/packages/runtime-vapor/src/componentSlots.ts @@ -1,4 +1,4 @@ -import { type IfAny, isArray } from '@vue/shared' +import { type IfAny, extend, isArray } from '@vue/shared' import { baseWatch } from '@vue/reactivity' import type { ComponentInternalInstance } from './component' import type { Block } from './apiRender' @@ -29,13 +29,7 @@ export const initSlots = ( rawSlots: InternalSlots | null = null, dynamicSlots: DinamicSlots | null = null, ) => { - const slots: InternalSlots = {} - for (const key in rawSlots) { - Object.defineProperty(slots, key, { - get: () => rawSlots[key], - enumerable: true, - }) - } + const slots: InternalSlots = extend({}, rawSlots) if (dynamicSlots) { const dynamicSlotKeys: Record<string, true> = {} From a8a7e4797a6009d7df8cf4a9d2ce256bcaa935fe Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sun, 24 Mar 2024 00:18:56 +0900 Subject: [PATCH 29/33] chore: typo (DinamicSlots -> DynamicSlots) --- packages/runtime-vapor/src/apiCreateComponent.ts | 4 ++-- packages/runtime-vapor/src/component.ts | 4 ++-- packages/runtime-vapor/src/componentSlots.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/runtime-vapor/src/apiCreateComponent.ts b/packages/runtime-vapor/src/apiCreateComponent.ts index fc6e62e17..cf282706b 100644 --- a/packages/runtime-vapor/src/apiCreateComponent.ts +++ b/packages/runtime-vapor/src/apiCreateComponent.ts @@ -5,14 +5,14 @@ import { } from './component' import { setupComponent } from './apiRender' import type { RawProps } from './componentProps' -import type { DinamicSlots, Slots } from './componentSlots' +import type { DynamicSlots, Slots } from './componentSlots' import { withAttrs } from './componentAttrs' export function createComponent( comp: Component, rawProps: RawProps | null = null, slots: Slots | null = null, - dynamicSlots: DinamicSlots | null = null, + dynamicSlots: DynamicSlots | null = null, singleRoot: boolean = false, ) { const current = currentInstance! diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 41b8396b0..683138339 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -18,7 +18,7 @@ import { normalizeEmitsOptions, } from './componentEmits' import { - type DinamicSlots, + type DynamicSlots, type InternalSlots, type Slots, initSlots, @@ -201,7 +201,7 @@ export function createComponentInstance( component: ObjectComponent | FunctionalComponent, rawProps: RawProps | null, slots: Slots | null = null, - dynamicSlots: DinamicSlots | null = null, + dynamicSlots: DynamicSlots | null = null, // application root node only appContext: AppContext | null = null, ): ComponentInternalInstance { diff --git a/packages/runtime-vapor/src/componentSlots.ts b/packages/runtime-vapor/src/componentSlots.ts index 29b84f4a1..4eba7abf7 100644 --- a/packages/runtime-vapor/src/componentSlots.ts +++ b/packages/runtime-vapor/src/componentSlots.ts @@ -22,12 +22,12 @@ export interface DynamicSlot { key?: string } -export type DinamicSlots = () => (DynamicSlot | DynamicSlot[])[] +export type DynamicSlots = () => (DynamicSlot | DynamicSlot[])[] export const initSlots = ( instance: ComponentInternalInstance, rawSlots: InternalSlots | null = null, - dynamicSlots: DinamicSlots | null = null, + dynamicSlots: DynamicSlots | null = null, ) => { const slots: InternalSlots = extend({}, rawSlots) From 08cc2bc41ecb24ecdaf2156ccf273856f30f5bd6 Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sun, 24 Mar 2024 00:31:11 +0900 Subject: [PATCH 30/33] chore: remove dead codes --- packages/runtime-vapor/src/component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 683138339..f2b2b9388 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -1,4 +1,4 @@ -import { EffectScope, TrackOpTypes, track } from '@vue/reactivity' +import { EffectScope } from '@vue/reactivity' import { EMPTY_OBJ, NOOP, isFunction } from '@vue/shared' import type { Block } from './apiRender' import type { DirectiveBinding } from './directives' From c6335341520bd085d326bfdf14f65d0978afc49b Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sun, 24 Mar 2024 00:54:36 +0900 Subject: [PATCH 31/33] chore(runtime-vapor): make slotsProxy optional --- packages/runtime-vapor/src/component.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index f2b2b9388..70d465392 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -116,7 +116,7 @@ export interface ComponentInternalInstance { refs: Data attrsProxy: Data | null - slotsProxy: Slots | null + slotsProxy?: Slots // lifecycle isMounted: boolean @@ -242,7 +242,6 @@ export function createComponentInstance( refs: EMPTY_OBJ, attrsProxy: null, - slotsProxy: null, // lifecycle isMounted: false, From 5b7620844bfa44653436c58f357fadaf83f49060 Mon Sep 17 00:00:00 2001 From: Ubugeeei <ubuge1122@gmail.com> Date: Sun, 24 Mar 2024 02:39:34 +0900 Subject: [PATCH 32/33] chore(runtime-vapor): make attrsProxy optional --- packages/runtime-vapor/src/component.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 70d465392..482be5661 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -115,7 +115,7 @@ export interface ComponentInternalInstance { slots: InternalSlots refs: Data - attrsProxy: Data | null + attrsProxy?: Data slotsProxy?: Slots // lifecycle @@ -241,8 +241,6 @@ export function createComponentInstance( slots: EMPTY_OBJ, refs: EMPTY_OBJ, - attrsProxy: null, - // lifecycle isMounted: false, isUnmounted: false, From 9510748eed55184b21d49e1efa7b4e38cbae6834 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= <sxzz@sxzz.moe> Date: Sun, 24 Mar 2024 20:28:41 +0800 Subject: [PATCH 33/33] chore: update --- .../__tests__/componentSlots.spec.ts | 56 +++++++++---------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/packages/runtime-vapor/__tests__/componentSlots.spec.ts b/packages/runtime-vapor/__tests__/componentSlots.spec.ts index d0252f57c..ec8788060 100644 --- a/packages/runtime-vapor/__tests__/componentSlots.spec.ts +++ b/packages/runtime-vapor/__tests__/componentSlots.spec.ts @@ -12,44 +12,42 @@ import { import { makeRender } from './_utils' const define = makeRender<any>() +function renderWithSlots(slots: any): any { + let instance: any + const Comp = defineComponent({ + render() { + const t0 = template('<div></div>') + const n0 = t0() + instance = getCurrentInstance() + return n0 + }, + }) -describe('component: slots', () => { - function renderWithSlots(slots: any): any { - let instance: any - const Comp = defineComponent({ - vapor: true, - render() { - const t0 = template('<div></div>') - const n0 = t0() - instance = getCurrentInstance() - return n0 - }, - }) - - const { render } = define({ - render() { - return createComponent(Comp, {}, slots) - }, - }) + const { render } = define({ + render() { + return createComponent(Comp, {}, slots) + }, + }) - render() - return instance - } + render() + return instance +} +describe('component: slots', () => { test('initSlots: instance.slots should be set correctly', () => { const { slots } = renderWithSlots({ _: 1 }) expect(slots).toMatchObject({ _: 1 }) }) // NOTE: slot normalization is not supported - // test.todo( - // 'initSlots: should normalize object slots (when value is null, string, array)', - // () => {}, - // ) - // test.todo( - // 'initSlots: should normalize object slots (when value is function)', - // () => {}, - // ) + test.todo( + 'initSlots: should normalize object slots (when value is null, string, array)', + () => {}, + ) + test.todo( + 'initSlots: should normalize object slots (when value is function)', + () => {}, + ) test('initSlots: instance.slots should be set correctly', () => { let instance: any