From bb26087b476547b74c49fb9e4698640f9bcec9b7 Mon Sep 17 00:00:00 2001 From: Doctorwu Date: Sun, 17 Mar 2024 14:51:21 +0800 Subject: [PATCH 01/32] feat(vapor): implement inheritAttrs --- packages/runtime-vapor/src/component.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index b980cc7ec..4b6ace41c 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -28,6 +28,7 @@ export type FunctionalComponent = SetupFn & Omit export interface ObjectComponent { props?: ComponentPropsOptions + inheritAttrs?: boolean emits?: EmitsOptions setup?: SetupFn render?(ctx: any): Block @@ -60,6 +61,11 @@ export interface ComponentInternalInstance { emitted: Record | null attrs: Data refs: Data + /** + * resolved inheritAttrs options + * @internal + */ + inheritAttrs?: boolean // lifecycle isMounted: boolean From 7e1f5927319518685a0316e178593a1e45a06158 Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Sun, 17 Mar 2024 15:41:18 +0800 Subject: [PATCH 02/32] feat(runtime-vapor): apply inheritAttrs to instance --- packages/runtime-vapor/src/component.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 4b6ace41c..e0ba8bd22 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -230,6 +230,9 @@ export function createComponentInstance( */ // [VaporLifecycleHooks.SERVER_PREFETCH]: null, } + if (component.inheritAttrs != null) { + instance.inheritAttrs = component.inheritAttrs + } initProps(instance, rawProps, !isFunction(component)) instance.emit = emit.bind(null, instance) From 9d8c1e5cbefc46056592af3991853431025f37f8 Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Sun, 17 Mar 2024 17:05:45 +0800 Subject: [PATCH 03/32] feat(runtime-vapor): extract apiSetup & init setup ctx --- .../runtime-vapor/src/apiCreateComponent.ts | 2 +- .../runtime-vapor/src/apiCreateVaporApp.ts | 3 +- packages/runtime-vapor/src/apiRender.ts | 41 +----------- packages/runtime-vapor/src/apiSetup.ts | 66 +++++++++++++++++++ packages/runtime-vapor/src/component.ts | 8 ++- packages/runtime-vapor/src/componentAttrs.ts | 46 ++++++++++++- 6 files changed, 120 insertions(+), 46 deletions(-) create mode 100644 packages/runtime-vapor/src/apiSetup.ts diff --git a/packages/runtime-vapor/src/apiCreateComponent.ts b/packages/runtime-vapor/src/apiCreateComponent.ts index c74783b32..54d2b53bd 100644 --- a/packages/runtime-vapor/src/apiCreateComponent.ts +++ b/packages/runtime-vapor/src/apiCreateComponent.ts @@ -3,7 +3,7 @@ import { createComponentInstance, currentInstance, } from './component' -import { setupComponent } from './apiRender' +import { setupComponent } from './apiSetup' import type { RawProps } from './componentProps' export function createComponent(comp: Component, rawProps: RawProps = null) { diff --git a/packages/runtime-vapor/src/apiCreateVaporApp.ts b/packages/runtime-vapor/src/apiCreateVaporApp.ts index af8e480fd..b4c24680e 100644 --- a/packages/runtime-vapor/src/apiCreateVaporApp.ts +++ b/packages/runtime-vapor/src/apiCreateVaporApp.ts @@ -6,8 +6,9 @@ import { } from './component' import { warn } from './warning' import { version } from '.' -import { render, setupComponent, unmountComponent } from './apiRender' +import { render, unmountComponent } from './apiRender' import type { RawProps } from './componentProps' +import { setupComponent } from './apiSetup' export function createVaporApp( rootComponent: Component, diff --git a/packages/runtime-vapor/src/apiRender.ts b/packages/runtime-vapor/src/apiRender.ts index 94efce891..e2f2f77fa 100644 --- a/packages/runtime-vapor/src/apiRender.ts +++ b/packages/runtime-vapor/src/apiRender.ts @@ -1,8 +1,6 @@ -import { isArray, isFunction, isObject } from '@vue/shared' -import { type ComponentInternalInstance, setCurrentInstance } from './component' +import type { ComponentInternalInstance } from './component' import { insert, querySelector, remove } from './dom/element' import { flushPostFlushCbs, queuePostRenderEffect } from './scheduler' -import { proxyRefs } from '@vue/reactivity' import { invokeLifecycle } from './componentLifecycle' import { VaporLifecycleHooks } from './apiLifecycle' @@ -15,43 +13,6 @@ export type Fragment = { [fragmentKey]: true } -export function setupComponent(instance: ComponentInternalInstance): void { - const reset = setCurrentInstance(instance) - instance.scope.run(() => { - const { component, props, emit, attrs } = instance - const ctx = { expose: () => {}, emit, attrs } - - const setupFn = isFunction(component) ? component : component.setup - const stateOrNode = setupFn && setupFn(props, ctx) - - let block: Block | undefined - - if ( - stateOrNode && - (stateOrNode instanceof Node || - isArray(stateOrNode) || - (stateOrNode as any)[fragmentKey]) - ) { - block = stateOrNode as Block - } else if (isObject(stateOrNode)) { - instance.setupState = proxyRefs(stateOrNode) - } - if (!block && component.render) { - block = component.render(instance.setupState) - } - - if (block instanceof DocumentFragment) { - block = Array.from(block.childNodes) - } - if (!block) { - // TODO: warn no template - block = [] - } - return (instance.block = block) - }) - reset() -} - export function render( instance: ComponentInternalInstance, container: string | ParentNode, diff --git a/packages/runtime-vapor/src/apiSetup.ts b/packages/runtime-vapor/src/apiSetup.ts new file mode 100644 index 000000000..cd4968bd0 --- /dev/null +++ b/packages/runtime-vapor/src/apiSetup.ts @@ -0,0 +1,66 @@ +import { proxyRefs } from '@vue/reactivity' +import { isArray, isFunction, isObject } from '@vue/shared' +import { type ComponentInternalInstance, setCurrentInstance } from './component' +import { getAttrsProxy } from './componentAttrs' +// import { SetupContext } from 'vue' +import { type Block, fragmentKey } from './apiRender' + +export function setupComponent(instance: ComponentInternalInstance): void { + const reset = setCurrentInstance(instance) + instance.scope.run(() => { + const { component, props } = instance + const ctx = createSetupContext(instance) + + const setupFn = isFunction(component) ? component : component.setup + const stateOrNode = setupFn && setupFn(props, ctx) + + let block: Block | undefined + + if ( + stateOrNode && + (stateOrNode instanceof Node || + isArray(stateOrNode) || + (stateOrNode as any)[fragmentKey]) + ) { + block = stateOrNode as Block + } else if (isObject(stateOrNode)) { + instance.setupState = proxyRefs(stateOrNode) + } + if (!block && component.render) { + block = component.render(instance.setupState) + } + + if (block instanceof DocumentFragment) { + block = Array.from(block.childNodes) + } + if (!block) { + // TODO: warn no template + block = [] + } + return (instance.block = block) + }) + reset() +} + +export function createSetupContext(instance: ComponentInternalInstance): any { + if (__DEV__) { + // We use getters in dev in case libs like test-utils overwrite instance + // properties (overwrites should not be done in prod) + return Object.freeze({ + expose: () => {}, + get attrs() { + return getAttrsProxy(instance) + }, + get emit() { + return (event: string, ...args: any[]) => instance.emit(event, ...args) + }, + }) + } else { + return { + get attrs() { + return getAttrsProxy(instance) + }, + emit: instance.emit, + } + } +} diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index e0ba8bd22..0ce2aad3d 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -67,6 +67,8 @@ export interface ComponentInternalInstance { */ inheritAttrs?: boolean + attrsProxy: Data | null + // lifecycle isMounted: boolean isUnmounted: boolean @@ -176,6 +178,9 @@ export function createComponentInstance( attrs: EMPTY_OBJ, refs: EMPTY_OBJ, + inheritAttrs: component.inheritAttrs, + attrsProxy: null, + // lifecycle isMounted: false, isUnmounted: false, @@ -230,9 +235,6 @@ export function createComponentInstance( */ // [VaporLifecycleHooks.SERVER_PREFETCH]: null, } - if (component.inheritAttrs != null) { - instance.inheritAttrs = component.inheritAttrs - } initProps(instance, rawProps, !isFunction(component)) instance.emit = emit.bind(null, instance) diff --git a/packages/runtime-vapor/src/componentAttrs.ts b/packages/runtime-vapor/src/componentAttrs.ts index 8eabb0449..ad9f4f115 100644 --- a/packages/runtime-vapor/src/componentAttrs.ts +++ b/packages/runtime-vapor/src/componentAttrs.ts @@ -1,6 +1,8 @@ -import { camelize, isFunction } from '@vue/shared' +import { type Data, camelize, isFunction } from '@vue/shared' import type { ComponentInternalInstance } from './component' import { isEmitListener } from './componentEmits' +import { TrackOpTypes, track } from '@vue/reactivity' +import { warn } from './warning' export function patchAttrs(instance: ComponentInternalInstance) { const attrs = instance.attrs @@ -42,3 +44,45 @@ export function patchAttrs(instance: ComponentInternalInstance) { } } } + +/** + * dev only flag to track whether $attrs was used during render. + * If $attrs was used during render then the warning for failed attrs + * fallthrough can be suppressed. + */ +let accessedAttrs: boolean = false + +function markAttrsAccessed() { + accessedAttrs = true +} + +export function getAttrsProxy(instance: ComponentInternalInstance): Data { + return ( + instance.attrsProxy || + (instance.attrsProxy = new Proxy( + instance.attrs, + __DEV__ + ? { + get(target, key: string) { + markAttrsAccessed() + track(instance, TrackOpTypes.GET, '$attrs') + return target[key] + }, + set() { + warn(`setupContext.attrs is readonly.`) + return false + }, + deleteProperty() { + warn(`setupContext.attrs is readonly.`) + return false + }, + } + : { + get(target, key: string) { + track(instance, TrackOpTypes.GET, '$attrs') + return target[key] + }, + }, + )) + ) +} From 20aa749dfd0b56737f014ff2a9963821d23e1bbf Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Sun, 17 Mar 2024 17:09:22 +0800 Subject: [PATCH 04/32] feat(runtime-vapor): add setup ctx type --- packages/runtime-vapor/src/apiSetup.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/runtime-vapor/src/apiSetup.ts b/packages/runtime-vapor/src/apiSetup.ts index cd4968bd0..b0602e504 100644 --- a/packages/runtime-vapor/src/apiSetup.ts +++ b/packages/runtime-vapor/src/apiSetup.ts @@ -1,9 +1,17 @@ import { proxyRefs } from '@vue/reactivity' -import { isArray, isFunction, isObject } from '@vue/shared' +import { type Data, isArray, isFunction, isObject } from '@vue/shared' import { type ComponentInternalInstance, setCurrentInstance } from './component' import { getAttrsProxy } from './componentAttrs' -// import { SetupContext } from 'vue' import { type Block, fragmentKey } from './apiRender' +import type { EmitFn, EmitsOptions } from './componentEmits' + +export type SetupContext = E extends any + ? { + attrs: Data + emit: EmitFn + expose: (exposed?: Record) => void + } + : never export function setupComponent(instance: ComponentInternalInstance): void { const reset = setCurrentInstance(instance) @@ -42,7 +50,9 @@ export function setupComponent(instance: ComponentInternalInstance): void { reset() } -export function createSetupContext(instance: ComponentInternalInstance): any { +export function createSetupContext( + instance: ComponentInternalInstance, +): SetupContext { if (__DEV__) { // We use getters in dev in case libs like test-utils overwrite instance // properties (overwrites should not be done in prod) @@ -57,6 +67,7 @@ export function createSetupContext(instance: ComponentInternalInstance): any { }) } else { return { + expose: () => {}, get attrs() { return getAttrsProxy(instance) }, From e4bc5ab256fc6add9f246a0f77477c1be5da29ac Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Sun, 17 Mar 2024 18:12:01 +0800 Subject: [PATCH 05/32] Revert "feat(runtime-vapor): add setup ctx type" This reverts commit b3851576c0f566fbb068342ef7de3a8a722b9e1d. --- packages/runtime-vapor/src/apiSetup.ts | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/packages/runtime-vapor/src/apiSetup.ts b/packages/runtime-vapor/src/apiSetup.ts index b0602e504..cd4968bd0 100644 --- a/packages/runtime-vapor/src/apiSetup.ts +++ b/packages/runtime-vapor/src/apiSetup.ts @@ -1,17 +1,9 @@ import { proxyRefs } from '@vue/reactivity' -import { type Data, isArray, isFunction, isObject } from '@vue/shared' +import { isArray, isFunction, isObject } from '@vue/shared' import { type ComponentInternalInstance, setCurrentInstance } from './component' import { getAttrsProxy } from './componentAttrs' +// import { SetupContext } from 'vue' import { type Block, fragmentKey } from './apiRender' -import type { EmitFn, EmitsOptions } from './componentEmits' - -export type SetupContext = E extends any - ? { - attrs: Data - emit: EmitFn - expose: (exposed?: Record) => void - } - : never export function setupComponent(instance: ComponentInternalInstance): void { const reset = setCurrentInstance(instance) @@ -50,9 +42,7 @@ export function setupComponent(instance: ComponentInternalInstance): void { reset() } -export function createSetupContext( - instance: ComponentInternalInstance, -): SetupContext { +export function createSetupContext(instance: ComponentInternalInstance): any { if (__DEV__) { // We use getters in dev in case libs like test-utils overwrite instance // properties (overwrites should not be done in prod) @@ -67,7 +57,6 @@ export function createSetupContext( }) } else { return { - expose: () => {}, get attrs() { return getAttrsProxy(instance) }, From 2f8b9af4e993a6ba44319e2db06a6ffb0fe89099 Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Sun, 17 Mar 2024 18:12:18 +0800 Subject: [PATCH 06/32] Revert "feat(runtime-vapor): extract apiSetup & init setup ctx" This reverts commit a0b5653a5f6acff71769763564d77bc468080923. --- .../runtime-vapor/src/apiCreateComponent.ts | 2 +- .../runtime-vapor/src/apiCreateVaporApp.ts | 3 +- packages/runtime-vapor/src/apiRender.ts | 41 +++++++++++- packages/runtime-vapor/src/apiSetup.ts | 66 ------------------- packages/runtime-vapor/src/component.ts | 8 +-- packages/runtime-vapor/src/componentAttrs.ts | 46 +------------ 6 files changed, 46 insertions(+), 120 deletions(-) delete mode 100644 packages/runtime-vapor/src/apiSetup.ts diff --git a/packages/runtime-vapor/src/apiCreateComponent.ts b/packages/runtime-vapor/src/apiCreateComponent.ts index 54d2b53bd..c74783b32 100644 --- a/packages/runtime-vapor/src/apiCreateComponent.ts +++ b/packages/runtime-vapor/src/apiCreateComponent.ts @@ -3,7 +3,7 @@ import { createComponentInstance, currentInstance, } from './component' -import { setupComponent } from './apiSetup' +import { setupComponent } from './apiRender' import type { RawProps } from './componentProps' export function createComponent(comp: Component, rawProps: RawProps = null) { diff --git a/packages/runtime-vapor/src/apiCreateVaporApp.ts b/packages/runtime-vapor/src/apiCreateVaporApp.ts index b4c24680e..af8e480fd 100644 --- a/packages/runtime-vapor/src/apiCreateVaporApp.ts +++ b/packages/runtime-vapor/src/apiCreateVaporApp.ts @@ -6,9 +6,8 @@ import { } from './component' import { warn } from './warning' import { version } from '.' -import { render, unmountComponent } from './apiRender' +import { render, setupComponent, unmountComponent } from './apiRender' import type { RawProps } from './componentProps' -import { setupComponent } from './apiSetup' export function createVaporApp( rootComponent: Component, diff --git a/packages/runtime-vapor/src/apiRender.ts b/packages/runtime-vapor/src/apiRender.ts index e2f2f77fa..94efce891 100644 --- a/packages/runtime-vapor/src/apiRender.ts +++ b/packages/runtime-vapor/src/apiRender.ts @@ -1,6 +1,8 @@ -import type { ComponentInternalInstance } from './component' +import { isArray, isFunction, isObject } from '@vue/shared' +import { type ComponentInternalInstance, setCurrentInstance } from './component' import { insert, querySelector, remove } from './dom/element' import { flushPostFlushCbs, queuePostRenderEffect } from './scheduler' +import { proxyRefs } from '@vue/reactivity' import { invokeLifecycle } from './componentLifecycle' import { VaporLifecycleHooks } from './apiLifecycle' @@ -13,6 +15,43 @@ export type Fragment = { [fragmentKey]: true } +export function setupComponent(instance: ComponentInternalInstance): void { + const reset = setCurrentInstance(instance) + instance.scope.run(() => { + const { component, props, emit, attrs } = instance + const ctx = { expose: () => {}, emit, attrs } + + const setupFn = isFunction(component) ? component : component.setup + const stateOrNode = setupFn && setupFn(props, ctx) + + let block: Block | undefined + + if ( + stateOrNode && + (stateOrNode instanceof Node || + isArray(stateOrNode) || + (stateOrNode as any)[fragmentKey]) + ) { + block = stateOrNode as Block + } else if (isObject(stateOrNode)) { + instance.setupState = proxyRefs(stateOrNode) + } + if (!block && component.render) { + block = component.render(instance.setupState) + } + + if (block instanceof DocumentFragment) { + block = Array.from(block.childNodes) + } + if (!block) { + // TODO: warn no template + block = [] + } + return (instance.block = block) + }) + reset() +} + export function render( instance: ComponentInternalInstance, container: string | ParentNode, diff --git a/packages/runtime-vapor/src/apiSetup.ts b/packages/runtime-vapor/src/apiSetup.ts deleted file mode 100644 index cd4968bd0..000000000 --- a/packages/runtime-vapor/src/apiSetup.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { proxyRefs } from '@vue/reactivity' -import { isArray, isFunction, isObject } from '@vue/shared' -import { type ComponentInternalInstance, setCurrentInstance } from './component' -import { getAttrsProxy } from './componentAttrs' -// import { SetupContext } from 'vue' -import { type Block, fragmentKey } from './apiRender' - -export function setupComponent(instance: ComponentInternalInstance): void { - const reset = setCurrentInstance(instance) - instance.scope.run(() => { - const { component, props } = instance - const ctx = createSetupContext(instance) - - const setupFn = isFunction(component) ? component : component.setup - const stateOrNode = setupFn && setupFn(props, ctx) - - let block: Block | undefined - - if ( - stateOrNode && - (stateOrNode instanceof Node || - isArray(stateOrNode) || - (stateOrNode as any)[fragmentKey]) - ) { - block = stateOrNode as Block - } else if (isObject(stateOrNode)) { - instance.setupState = proxyRefs(stateOrNode) - } - if (!block && component.render) { - block = component.render(instance.setupState) - } - - if (block instanceof DocumentFragment) { - block = Array.from(block.childNodes) - } - if (!block) { - // TODO: warn no template - block = [] - } - return (instance.block = block) - }) - reset() -} - -export function createSetupContext(instance: ComponentInternalInstance): any { - if (__DEV__) { - // We use getters in dev in case libs like test-utils overwrite instance - // properties (overwrites should not be done in prod) - return Object.freeze({ - expose: () => {}, - get attrs() { - return getAttrsProxy(instance) - }, - get emit() { - return (event: string, ...args: any[]) => instance.emit(event, ...args) - }, - }) - } else { - return { - get attrs() { - return getAttrsProxy(instance) - }, - emit: instance.emit, - } - } -} diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 0ce2aad3d..e0ba8bd22 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -67,8 +67,6 @@ export interface ComponentInternalInstance { */ inheritAttrs?: boolean - attrsProxy: Data | null - // lifecycle isMounted: boolean isUnmounted: boolean @@ -178,9 +176,6 @@ export function createComponentInstance( attrs: EMPTY_OBJ, refs: EMPTY_OBJ, - inheritAttrs: component.inheritAttrs, - attrsProxy: null, - // lifecycle isMounted: false, isUnmounted: false, @@ -235,6 +230,9 @@ export function createComponentInstance( */ // [VaporLifecycleHooks.SERVER_PREFETCH]: null, } + if (component.inheritAttrs != null) { + instance.inheritAttrs = component.inheritAttrs + } initProps(instance, rawProps, !isFunction(component)) instance.emit = emit.bind(null, instance) diff --git a/packages/runtime-vapor/src/componentAttrs.ts b/packages/runtime-vapor/src/componentAttrs.ts index ad9f4f115..8eabb0449 100644 --- a/packages/runtime-vapor/src/componentAttrs.ts +++ b/packages/runtime-vapor/src/componentAttrs.ts @@ -1,8 +1,6 @@ -import { type Data, camelize, isFunction } from '@vue/shared' +import { camelize, isFunction } from '@vue/shared' import type { ComponentInternalInstance } from './component' import { isEmitListener } from './componentEmits' -import { TrackOpTypes, track } from '@vue/reactivity' -import { warn } from './warning' export function patchAttrs(instance: ComponentInternalInstance) { const attrs = instance.attrs @@ -44,45 +42,3 @@ export function patchAttrs(instance: ComponentInternalInstance) { } } } - -/** - * dev only flag to track whether $attrs was used during render. - * If $attrs was used during render then the warning for failed attrs - * fallthrough can be suppressed. - */ -let accessedAttrs: boolean = false - -function markAttrsAccessed() { - accessedAttrs = true -} - -export function getAttrsProxy(instance: ComponentInternalInstance): Data { - return ( - instance.attrsProxy || - (instance.attrsProxy = new Proxy( - instance.attrs, - __DEV__ - ? { - get(target, key: string) { - markAttrsAccessed() - track(instance, TrackOpTypes.GET, '$attrs') - return target[key] - }, - set() { - warn(`setupContext.attrs is readonly.`) - return false - }, - deleteProperty() { - warn(`setupContext.attrs is readonly.`) - return false - }, - } - : { - get(target, key: string) { - track(instance, TrackOpTypes.GET, '$attrs') - return target[key] - }, - }, - )) - ) -} From 757aa74ef3bd8a6b426983650205ebd3b2c5e4bb Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Sun, 17 Mar 2024 18:44:54 +0800 Subject: [PATCH 07/32] feat(runtime-vapor): impl fallthrough attrs --- packages/runtime-vapor/src/apiRender.ts | 7 ++++++- packages/runtime-vapor/src/componentAttrs.ts | 20 +++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/packages/runtime-vapor/src/apiRender.ts b/packages/runtime-vapor/src/apiRender.ts index 94efce891..72674d662 100644 --- a/packages/runtime-vapor/src/apiRender.ts +++ b/packages/runtime-vapor/src/apiRender.ts @@ -5,6 +5,7 @@ import { flushPostFlushCbs, queuePostRenderEffect } from './scheduler' import { proxyRefs } from '@vue/reactivity' import { invokeLifecycle } from './componentLifecycle' import { VaporLifecycleHooks } from './apiLifecycle' +import { fallThroughAttrs } from './componentAttrs' export const fragmentKey = Symbol(__DEV__ ? `fragmentKey` : ``) @@ -47,7 +48,11 @@ export function setupComponent(instance: ComponentInternalInstance): void { // TODO: warn no template block = [] } - return (instance.block = block) + instance.block = block + if (instance.inheritAttrs !== false) { + fallThroughAttrs(instance) + } + return block }) reset() } diff --git a/packages/runtime-vapor/src/componentAttrs.ts b/packages/runtime-vapor/src/componentAttrs.ts index 8eabb0449..565fb324b 100644 --- a/packages/runtime-vapor/src/componentAttrs.ts +++ b/packages/runtime-vapor/src/componentAttrs.ts @@ -1,6 +1,7 @@ -import { camelize, isFunction } from '@vue/shared' +import { camelize, isArray, isFunction } from '@vue/shared' import type { ComponentInternalInstance } from './component' import { isEmitListener } from './componentEmits' +import type { Block } from './apiRender' export function patchAttrs(instance: ComponentInternalInstance) { const attrs = instance.attrs @@ -42,3 +43,20 @@ export function patchAttrs(instance: ComponentInternalInstance) { } } } + +export function fallThroughAttrs(instance: ComponentInternalInstance) { + const attrs = instance.attrs + const block = getFallThroughNode(instance.block!) + if (!block) return + for (const key in attrs) { + if (block instanceof Element) { + block.setAttribute(key, String(attrs[key])) + } + } +} + +function getFallThroughNode(block: Block) { + if (block instanceof Node) return block + if (isArray(block) && block.length === 1) return getFallThroughNode(block[0]) + return null +} From 2db5e1902673a649293cdfb7f1419ac20244ea8a Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Sun, 17 Mar 2024 18:53:25 +0800 Subject: [PATCH 08/32] test(runtime-vapor): tweak props test case --- packages/runtime-vapor/__tests__/componentProps.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/runtime-vapor/__tests__/componentProps.spec.ts b/packages/runtime-vapor/__tests__/componentProps.spec.ts index 12f97274a..98191d898 100644 --- a/packages/runtime-vapor/__tests__/componentProps.spec.ts +++ b/packages/runtime-vapor/__tests__/componentProps.spec.ts @@ -222,6 +222,7 @@ describe('component: props', () => { test('optimized props updates', async () => { const t0 = template('
') const { component: Child } = define({ + inheritAttrs: false, props: ['foo'], render() { const instance = getCurrentInstance()! @@ -441,6 +442,7 @@ describe('component: props', () => { // #5016 test('handling attr with undefined value', () => { const { render, host } = define({ + inheritAttrs: false, render() { const instance = getCurrentInstance()! const t0 = template('
') From 8498ba6eb16eda707b1c4d16e66946e8963e18f0 Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Sun, 17 Mar 2024 22:26:27 +0800 Subject: [PATCH 09/32] feat(runtime-vapor): update attrs when props update --- .../__tests__/componentAttrs.spec.ts | 56 +++++++++++++++++++ .../__tests__/componentProps.spec.ts | 3 - packages/runtime-vapor/src/apiRender.ts | 12 +++- 3 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 packages/runtime-vapor/__tests__/componentAttrs.spec.ts diff --git a/packages/runtime-vapor/__tests__/componentAttrs.spec.ts b/packages/runtime-vapor/__tests__/componentAttrs.spec.ts new file mode 100644 index 000000000..ac14c176e --- /dev/null +++ b/packages/runtime-vapor/__tests__/componentAttrs.spec.ts @@ -0,0 +1,56 @@ +import { + createComponent, + defineComponent, + getCurrentInstance, + nextTick, + ref, + setText, + template, + toRefs, + watch, + watchEffect, +} from '../src' +import { setCurrentInstance } from '../src/component' +import { makeRender } from './_utils' + +const define = makeRender() + +describe('attribute fallthrough', () => { + it('should allow attrs to fallthrough', async () => { + const t0 = template('
') + const { component: Child } = define({ + props: ['foo'], + render() { + const instance = getCurrentInstance()! + const n0 = t0() + watchEffect(() => setText(n0, instance.props.foo)) + return n0 + }, + }) + + const foo = ref(1) + const id = ref('a') + const { instance, host } = define({ + setup() { + return { foo, id } + }, + render(_ctx: Record) { + return createComponent(Child, { + foo: () => _ctx.foo, + id: () => _ctx.id, + }) + }, + }).render() + const reset = setCurrentInstance(instance) + expect(host.innerHTML).toBe('
1
') + + foo.value++ + await nextTick() + expect(host.innerHTML).toBe('
2
') + + id.value = 'b' + await nextTick() + expect(host.innerHTML).toBe('
2
') + reset() + }) +}) diff --git a/packages/runtime-vapor/__tests__/componentProps.spec.ts b/packages/runtime-vapor/__tests__/componentProps.spec.ts index 98191d898..87aa15f80 100644 --- a/packages/runtime-vapor/__tests__/componentProps.spec.ts +++ b/packages/runtime-vapor/__tests__/componentProps.spec.ts @@ -246,17 +246,14 @@ describe('component: props', () => { }, }).render() const reset = setCurrentInstance(instance) - // expect(host.innerHTML).toBe('
1
') // TODO: Fallthrough Attributes expect(host.innerHTML).toBe('
1
') foo.value++ await nextTick() - // expect(host.innerHTML).toBe('
2
') // TODO: Fallthrough Attributes expect(host.innerHTML).toBe('
2
') id.value = 'b' await nextTick() - // expect(host.innerHTML).toBe('
2
') // TODO: Fallthrough Attributes reset() }) diff --git a/packages/runtime-vapor/src/apiRender.ts b/packages/runtime-vapor/src/apiRender.ts index 72674d662..4cf8001f9 100644 --- a/packages/runtime-vapor/src/apiRender.ts +++ b/packages/runtime-vapor/src/apiRender.ts @@ -1,8 +1,12 @@ import { isArray, isFunction, isObject } from '@vue/shared' import { type ComponentInternalInstance, setCurrentInstance } from './component' import { insert, querySelector, remove } from './dom/element' -import { flushPostFlushCbs, queuePostRenderEffect } from './scheduler' -import { proxyRefs } from '@vue/reactivity' +import { + createVaporPreScheduler, + flushPostFlushCbs, + queuePostRenderEffect, +} from './scheduler' +import { baseWatch, proxyRefs } from '@vue/reactivity' import { invokeLifecycle } from './componentLifecycle' import { VaporLifecycleHooks } from './apiLifecycle' import { fallThroughAttrs } from './componentAttrs' @@ -50,7 +54,9 @@ export function setupComponent(instance: ComponentInternalInstance): void { } instance.block = block if (instance.inheritAttrs !== false) { - fallThroughAttrs(instance) + baseWatch(() => fallThroughAttrs(instance), undefined, { + scheduler: createVaporPreScheduler(instance), + }) } return block }) From 1742efed18dd74c1a51b3360a4492a408e6736bf Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Mon, 18 Mar 2024 10:20:09 +0800 Subject: [PATCH 10/32] feat(runtime-vapor): remove unecessary property & use proper way to set attribute --- packages/runtime-vapor/src/apiRender.ts | 2 +- packages/runtime-vapor/src/component.ts | 8 -------- packages/runtime-vapor/src/componentAttrs.ts | 3 ++- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/packages/runtime-vapor/src/apiRender.ts b/packages/runtime-vapor/src/apiRender.ts index 4cf8001f9..5a2390baf 100644 --- a/packages/runtime-vapor/src/apiRender.ts +++ b/packages/runtime-vapor/src/apiRender.ts @@ -53,7 +53,7 @@ export function setupComponent(instance: ComponentInternalInstance): void { block = [] } instance.block = block - if (instance.inheritAttrs !== false) { + if (instance.component.inheritAttrs !== false) { baseWatch(() => fallThroughAttrs(instance), undefined, { scheduler: createVaporPreScheduler(instance), }) diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index e0ba8bd22..2efd46d3b 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -61,11 +61,6 @@ export interface ComponentInternalInstance { emitted: Record | null attrs: Data refs: Data - /** - * resolved inheritAttrs options - * @internal - */ - inheritAttrs?: boolean // lifecycle isMounted: boolean @@ -230,9 +225,6 @@ export function createComponentInstance( */ // [VaporLifecycleHooks.SERVER_PREFETCH]: null, } - if (component.inheritAttrs != null) { - instance.inheritAttrs = component.inheritAttrs - } initProps(instance, rawProps, !isFunction(component)) instance.emit = emit.bind(null, instance) diff --git a/packages/runtime-vapor/src/componentAttrs.ts b/packages/runtime-vapor/src/componentAttrs.ts index 565fb324b..cbc449b5a 100644 --- a/packages/runtime-vapor/src/componentAttrs.ts +++ b/packages/runtime-vapor/src/componentAttrs.ts @@ -2,6 +2,7 @@ import { camelize, isArray, isFunction } from '@vue/shared' import type { ComponentInternalInstance } from './component' import { isEmitListener } from './componentEmits' import type { Block } from './apiRender' +import { setDynamicProp } from './dom/prop' export function patchAttrs(instance: ComponentInternalInstance) { const attrs = instance.attrs @@ -50,7 +51,7 @@ export function fallThroughAttrs(instance: ComponentInternalInstance) { if (!block) return for (const key in attrs) { if (block instanceof Element) { - block.setAttribute(key, String(attrs[key])) + setDynamicProp(block, key, attrs[key]) } } } From e082299c44f898f90785b535a8b6b8989570d683 Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Mon, 18 Mar 2024 15:26:47 +0800 Subject: [PATCH 11/32] feat(runtime-vapor, compiler-vapor): add withAttr in singleRoot Component --- .../src/generators/component.ts | 16 +++++++++++++--- .../runtime-vapor/src/helpers/withAttrs.ts | 19 +++++++++++++++++++ packages/runtime-vapor/src/index.ts | 1 + 3 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 packages/runtime-vapor/src/helpers/withAttrs.ts diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index ad54d884f..126a40fcf 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -16,20 +16,30 @@ export function genCreateComponent( oper: CreateComponentIRNode, context: CodegenContext, ): CodeFragment[] { - const { vaporHelper } = context + const { + vaporHelper, + ir: { + node: { children }, + }, + } = context const tag = oper.resolve ? genCall(vaporHelper('resolveComponent'), JSON.stringify(oper.tag)) : [oper.tag] + let props = genProps() + if (children.length === 1) { + props = genCall(vaporHelper('withAttrs'), props) + } + return [ NEWLINE, `const n${oper.id} = `, - ...genCall(vaporHelper('createComponent'), tag, genProps()), + ...genCall(vaporHelper('createComponent'), tag, props), ] function genProps() { - const props = oper.props + const props = [...oper.props] .map(props => { if (isArray(props)) { if (!props.length) return undefined diff --git a/packages/runtime-vapor/src/helpers/withAttrs.ts b/packages/runtime-vapor/src/helpers/withAttrs.ts new file mode 100644 index 000000000..efafe4caf --- /dev/null +++ b/packages/runtime-vapor/src/helpers/withAttrs.ts @@ -0,0 +1,19 @@ +import type { Data } from '@vue/shared' +import { currentInstance } from '../component' +import type { RawProps } from '../componentProps' + +export function withAttrs(props: RawProps) { + const instance = currentInstance! + if (!props) return transformAttrs(instance.attrs) + if (Array.isArray(props)) { + return [transformAttrs(instance.attrs), ...props] + } + return [transformAttrs(instance.attrs), props] +} + +function transformAttrs(attrs: Data) { + return Object.keys(attrs).reduce((acc, key) => { + acc[key] = () => attrs[key] + return acc + }, {} as Data) +} diff --git a/packages/runtime-vapor/src/index.ts b/packages/runtime-vapor/src/index.ts index b5687fc1a..a730e15c2 100644 --- a/packages/runtime-vapor/src/index.ts +++ b/packages/runtime-vapor/src/index.ts @@ -111,6 +111,7 @@ export { createFor } from './apiCreateFor' export { createComponent } from './apiCreateComponent' export { resolveComponent, resolveDirective } from './helpers/resolveAssets' +export { withAttrs } from './helpers/withAttrs' // **Internal** DOM-only runtime directive helpers export { From 5cab771f619a2f720032ca758bb9158ac5dddcef Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Mon, 18 Mar 2024 16:05:16 +0800 Subject: [PATCH 12/32] feat(runtime-vapor, compiler-vapor): resolve nest component attrs --- packages/compiler-vapor/src/generators/component.ts | 9 ++++++++- packages/runtime-vapor/src/apiRender.ts | 6 +++++- packages/runtime-vapor/src/componentAttrs.ts | 5 ++++- packages/runtime-vapor/src/helpers/withAttrs.ts | 5 +++++ packages/runtime-vapor/src/index.ts | 2 +- 5 files changed, 23 insertions(+), 4 deletions(-) diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index 126a40fcf..3d9428452 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -27,8 +27,9 @@ export function genCreateComponent( ? genCall(vaporHelper('resolveComponent'), JSON.stringify(oper.tag)) : [oper.tag] + const singleRoot = children.length === 1 let props = genProps() - if (children.length === 1) { + if (singleRoot) { props = genCall(vaporHelper('withAttrs'), props) } @@ -36,6 +37,12 @@ export function genCreateComponent( NEWLINE, `const n${oper.id} = `, ...genCall(vaporHelper('createComponent'), tag, props), + ...(singleRoot + ? [ + NEWLINE as typeof NEWLINE, + ...genCall(vaporHelper('markWithAttrs'), `n${oper.id}`), + ] + : []), ] function genProps() { diff --git a/packages/runtime-vapor/src/apiRender.ts b/packages/runtime-vapor/src/apiRender.ts index 5a2390baf..2545008c9 100644 --- a/packages/runtime-vapor/src/apiRender.ts +++ b/packages/runtime-vapor/src/apiRender.ts @@ -12,13 +12,17 @@ import { VaporLifecycleHooks } from './apiLifecycle' import { fallThroughAttrs } from './componentAttrs' export const fragmentKey = Symbol(__DEV__ ? `fragmentKey` : ``) +export const withAttrsKey = Symbol(__DEV__ ? `withAttrsKey` : ``) -export type Block = Node | Fragment | Block[] +export type Block = Node | WithAttrsNode | Fragment | Block[] export type Fragment = { nodes: Block anchor?: Node [fragmentKey]: true } +export type WithAttrsNode = Node & { + [withAttrsKey]: true +} export function setupComponent(instance: ComponentInternalInstance): void { const reset = setCurrentInstance(instance) diff --git a/packages/runtime-vapor/src/componentAttrs.ts b/packages/runtime-vapor/src/componentAttrs.ts index cbc449b5a..504c3b442 100644 --- a/packages/runtime-vapor/src/componentAttrs.ts +++ b/packages/runtime-vapor/src/componentAttrs.ts @@ -1,7 +1,7 @@ import { camelize, isArray, isFunction } from '@vue/shared' import type { ComponentInternalInstance } from './component' import { isEmitListener } from './componentEmits' -import type { Block } from './apiRender' +import { type Block, withAttrsKey } from './apiRender' import { setDynamicProp } from './dom/prop' export function patchAttrs(instance: ComponentInternalInstance) { @@ -57,6 +57,9 @@ export function fallThroughAttrs(instance: ComponentInternalInstance) { } function getFallThroughNode(block: Block) { + // If the block has withAttrsKey, it should not fall through In runtime + // since attrs was already pass through by props + if (withAttrsKey in block && block[withAttrsKey]) return null if (block instanceof Node) return block if (isArray(block) && block.length === 1) return getFallThroughNode(block[0]) return null diff --git a/packages/runtime-vapor/src/helpers/withAttrs.ts b/packages/runtime-vapor/src/helpers/withAttrs.ts index efafe4caf..69deeccf8 100644 --- a/packages/runtime-vapor/src/helpers/withAttrs.ts +++ b/packages/runtime-vapor/src/helpers/withAttrs.ts @@ -1,6 +1,7 @@ import type { Data } from '@vue/shared' import { currentInstance } from '../component' import type { RawProps } from '../componentProps' +import { type WithAttrsNode, withAttrsKey } from '../apiRender' export function withAttrs(props: RawProps) { const instance = currentInstance! @@ -11,6 +12,10 @@ export function withAttrs(props: RawProps) { return [transformAttrs(instance.attrs), props] } +export function markWithAttrs(node: WithAttrsNode) { + node[withAttrsKey] = true +} + function transformAttrs(attrs: Data) { return Object.keys(attrs).reduce((acc, key) => { acc[key] = () => attrs[key] diff --git a/packages/runtime-vapor/src/index.ts b/packages/runtime-vapor/src/index.ts index a730e15c2..c034071c8 100644 --- a/packages/runtime-vapor/src/index.ts +++ b/packages/runtime-vapor/src/index.ts @@ -111,7 +111,7 @@ export { createFor } from './apiCreateFor' export { createComponent } from './apiCreateComponent' export { resolveComponent, resolveDirective } from './helpers/resolveAssets' -export { withAttrs } from './helpers/withAttrs' +export { withAttrs, markWithAttrs } from './helpers/withAttrs' // **Internal** DOM-only runtime directive helpers export { From 1482a20b82a0ebef51614212e08c0b932fd92eb9 Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Mon, 18 Mar 2024 16:10:22 +0800 Subject: [PATCH 13/32] feat(compiler-vapor): remove unecessary clone props --- packages/compiler-vapor/src/generators/component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index 3d9428452..0fee9a0e6 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -46,7 +46,7 @@ export function genCreateComponent( ] function genProps() { - const props = [...oper.props] + const props = oper.props .map(props => { if (isArray(props)) { if (!props.length) return undefined From b3b66223fc1772752385b571fccb0e72ce78784c Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Mon, 18 Mar 2024 16:34:08 +0800 Subject: [PATCH 14/32] feat(runtime-vapor): clean code --- packages/runtime-vapor/src/componentAttrs.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/runtime-vapor/src/componentAttrs.ts b/packages/runtime-vapor/src/componentAttrs.ts index 504c3b442..ea407a7dd 100644 --- a/packages/runtime-vapor/src/componentAttrs.ts +++ b/packages/runtime-vapor/src/componentAttrs.ts @@ -61,6 +61,5 @@ function getFallThroughNode(block: Block) { // since attrs was already pass through by props if (withAttrsKey in block && block[withAttrsKey]) return null if (block instanceof Node) return block - if (isArray(block) && block.length === 1) return getFallThroughNode(block[0]) return null } From 791f533298f5c420751296db5cf4a1feabe7eb96 Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Mon, 18 Mar 2024 16:36:51 +0800 Subject: [PATCH 15/32] chore(runtime-vapor): make lint happy --- packages/runtime-vapor/src/componentAttrs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime-vapor/src/componentAttrs.ts b/packages/runtime-vapor/src/componentAttrs.ts index ea407a7dd..c7238f403 100644 --- a/packages/runtime-vapor/src/componentAttrs.ts +++ b/packages/runtime-vapor/src/componentAttrs.ts @@ -1,4 +1,4 @@ -import { camelize, isArray, isFunction } from '@vue/shared' +import { camelize, isFunction } from '@vue/shared' import type { ComponentInternalInstance } from './component' import { isEmitListener } from './componentEmits' import { type Block, withAttrsKey } from './apiRender' From f7d4c13bff1a95bf663b1605c14524d91a55dc7f Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Mon, 18 Mar 2024 17:45:53 +0800 Subject: [PATCH 16/32] feat(runtime-vapor, compiler-vapor): make inheritAttrs reactive --- .../src/generators/component.ts | 13 +- .../__tests__/componentAttrs.spec.ts | 115 ++++++++++++++++-- .../runtime-vapor/src/apiCreateComponent.ts | 13 +- packages/runtime-vapor/src/apiRender.ts | 14 ++- packages/runtime-vapor/src/componentAttrs.ts | 9 +- .../runtime-vapor/src/helpers/withAttrs.ts | 21 +--- packages/runtime-vapor/src/index.ts | 2 +- 7 files changed, 144 insertions(+), 43 deletions(-) diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index 0fee9a0e6..fc4185842 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -36,13 +36,12 @@ export function genCreateComponent( return [ NEWLINE, `const n${oper.id} = `, - ...genCall(vaporHelper('createComponent'), tag, props), - ...(singleRoot - ? [ - NEWLINE as typeof NEWLINE, - ...genCall(vaporHelper('markWithAttrs'), `n${oper.id}`), - ] - : []), + ...genCall( + vaporHelper('createComponent'), + tag, + props, + singleRoot ? 'true' : undefined, + ), ] function genProps() { diff --git a/packages/runtime-vapor/__tests__/componentAttrs.spec.ts b/packages/runtime-vapor/__tests__/componentAttrs.spec.ts index ac14c176e..e197fd1ff 100644 --- a/packages/runtime-vapor/__tests__/componentAttrs.spec.ts +++ b/packages/runtime-vapor/__tests__/componentAttrs.spec.ts @@ -1,15 +1,14 @@ import { createComponent, - defineComponent, getCurrentInstance, nextTick, ref, setText, template, - toRefs, - watch, watchEffect, + withAttrs, } from '../src' +import { WithAttrsNode } from '../src/apiRender' import { setCurrentInstance } from '../src/component' import { makeRender } from './_utils' @@ -35,10 +34,14 @@ describe('attribute fallthrough', () => { return { foo, id } }, render(_ctx: Record) { - return createComponent(Child, { - foo: () => _ctx.foo, - id: () => _ctx.id, - }) + return createComponent( + Child, + { + foo: () => _ctx.foo, + id: () => _ctx.id, + }, + true, + ) }, }).render() const reset = setCurrentInstance(instance) @@ -53,4 +56,102 @@ describe('attribute fallthrough', () => { expect(host.innerHTML).toBe('
2
') reset() }) + + it('should not fallthrough if explicitly pass inheritAttrs: false', async () => { + const t0 = template('
') + const { component: Child } = define({ + props: ['foo'], + inheritAttrs: false, + render() { + const instance = getCurrentInstance()! + const n0 = t0() + watchEffect(() => setText(n0, instance.props.foo)) + return n0 + }, + }) + + const foo = ref(1) + const id = ref('a') + const { instance, host } = define({ + setup() { + return { foo, id } + }, + render(_ctx: Record) { + return createComponent( + Child, + { + foo: () => _ctx.foo, + id: () => _ctx.id, + }, + true, + ) + }, + }).render() + const reset = setCurrentInstance(instance) + expect(host.innerHTML).toBe('
1
') + + foo.value++ + await nextTick() + expect(host.innerHTML).toBe('
2
') + + id.value = 'b' + await nextTick() + expect(host.innerHTML).toBe('
2
') + reset() + }) + + it('should pass through attrs in nested single root components', async () => { + const t0 = template('
') + const { component: Grandson } = define({ + props: ['custom-attr'], + render() { + const instance = getCurrentInstance()! + const n0 = t0() + watchEffect(() => setText(n0, instance.attrs.foo)) + return n0 + }, + }) + + const { component: Child } = define({ + render() { + const n0 = createComponent( + Grandson, + withAttrs({ + 'custom-attr': () => 'custom-attr', + }), + true, + ) + return n0 + }, + }) + + const foo = ref(1) + const id = ref('a') + const { instance, host } = define({ + setup() { + return { foo, id } + }, + render(_ctx: Record) { + return createComponent( + Child, + { + foo: () => _ctx.foo, + id: () => _ctx.id, + }, + true, + ) + }, + }).render() + const reset = setCurrentInstance(instance) + expect(host.innerHTML).toBe('
1
') + + foo.value++ + await nextTick() + expect(host.innerHTML).toBe('
2
') + + id.value = 'b' + await nextTick() + expect(host.innerHTML).toBe('
2
') + reset() + }) }) diff --git a/packages/runtime-vapor/src/apiCreateComponent.ts b/packages/runtime-vapor/src/apiCreateComponent.ts index c74783b32..d1d51bcac 100644 --- a/packages/runtime-vapor/src/apiCreateComponent.ts +++ b/packages/runtime-vapor/src/apiCreateComponent.ts @@ -3,13 +3,20 @@ import { createComponentInstance, currentInstance, } from './component' -import { setupComponent } from './apiRender' +import { type WithAttrsNode, setupComponent, withAttrsKey } from './apiRender' import type { RawProps } from './componentProps' -export function createComponent(comp: Component, rawProps: RawProps = null) { +export function createComponent( + comp: Component, + rawProps: RawProps = null, + withAttrs: boolean = false, +) { const current = currentInstance! const instance = createComponentInstance(comp, rawProps) - setupComponent(instance) + if (instance.block && withAttrs && !(withAttrsKey in instance.block)) { + ;(instance.block as WithAttrsNode)[withAttrsKey] = instance.uid + } + setupComponent(instance, withAttrs) // register sub-component with current component for lifecycle management current.comps.add(instance) diff --git a/packages/runtime-vapor/src/apiRender.ts b/packages/runtime-vapor/src/apiRender.ts index 2545008c9..c8f54f713 100644 --- a/packages/runtime-vapor/src/apiRender.ts +++ b/packages/runtime-vapor/src/apiRender.ts @@ -21,10 +21,13 @@ export type Fragment = { [fragmentKey]: true } export type WithAttrsNode = Node & { - [withAttrsKey]: true + [withAttrsKey]: number } -export function setupComponent(instance: ComponentInternalInstance): void { +export function setupComponent( + instance: ComponentInternalInstance, + withAttrs: boolean = false, +): void { const reset = setCurrentInstance(instance) instance.scope.run(() => { const { component, props, emit, attrs } = instance @@ -57,6 +60,13 @@ export function setupComponent(instance: ComponentInternalInstance): void { block = [] } instance.block = block + if ( + !instance.component.inheritAttrs && + withAttrs && + !(withAttrsKey in block) + ) { + ;(block as WithAttrsNode)[withAttrsKey] = instance.uid + } if (instance.component.inheritAttrs !== false) { baseWatch(() => fallThroughAttrs(instance), undefined, { scheduler: createVaporPreScheduler(instance), diff --git a/packages/runtime-vapor/src/componentAttrs.ts b/packages/runtime-vapor/src/componentAttrs.ts index c7238f403..7121c2956 100644 --- a/packages/runtime-vapor/src/componentAttrs.ts +++ b/packages/runtime-vapor/src/componentAttrs.ts @@ -47,7 +47,7 @@ export function patchAttrs(instance: ComponentInternalInstance) { export function fallThroughAttrs(instance: ComponentInternalInstance) { const attrs = instance.attrs - const block = getFallThroughNode(instance.block!) + const block = getFallThroughNode(instance.block!, instance.uid) if (!block) return for (const key in attrs) { if (block instanceof Element) { @@ -56,10 +56,7 @@ export function fallThroughAttrs(instance: ComponentInternalInstance) { } } -function getFallThroughNode(block: Block) { - // If the block has withAttrsKey, it should not fall through In runtime - // since attrs was already pass through by props - if (withAttrsKey in block && block[withAttrsKey]) return null - if (block instanceof Node) return block +function getFallThroughNode(block: Block, id: number) { + if (withAttrsKey in block && block[withAttrsKey] === id) return block return null } diff --git a/packages/runtime-vapor/src/helpers/withAttrs.ts b/packages/runtime-vapor/src/helpers/withAttrs.ts index 69deeccf8..6076858e1 100644 --- a/packages/runtime-vapor/src/helpers/withAttrs.ts +++ b/packages/runtime-vapor/src/helpers/withAttrs.ts @@ -1,24 +1,11 @@ -import type { Data } from '@vue/shared' import { currentInstance } from '../component' import type { RawProps } from '../componentProps' -import { type WithAttrsNode, withAttrsKey } from '../apiRender' -export function withAttrs(props: RawProps) { +export function withAttrs(props: RawProps): RawProps { const instance = currentInstance! - if (!props) return transformAttrs(instance.attrs) + if (!props) return [() => instance.attrs] if (Array.isArray(props)) { - return [transformAttrs(instance.attrs), ...props] + return [() => instance.attrs, ...props] } - return [transformAttrs(instance.attrs), props] -} - -export function markWithAttrs(node: WithAttrsNode) { - node[withAttrsKey] = true -} - -function transformAttrs(attrs: Data) { - return Object.keys(attrs).reduce((acc, key) => { - acc[key] = () => attrs[key] - return acc - }, {} as Data) + return [() => instance.attrs, props] } diff --git a/packages/runtime-vapor/src/index.ts b/packages/runtime-vapor/src/index.ts index c034071c8..a730e15c2 100644 --- a/packages/runtime-vapor/src/index.ts +++ b/packages/runtime-vapor/src/index.ts @@ -111,7 +111,7 @@ export { createFor } from './apiCreateFor' export { createComponent } from './apiCreateComponent' export { resolveComponent, resolveDirective } from './helpers/resolveAssets' -export { withAttrs, markWithAttrs } from './helpers/withAttrs' +export { withAttrs } from './helpers/withAttrs' // **Internal** DOM-only runtime directive helpers export { From bdc1ee41ce2724e8bb3b975a21936ef6e4527fe1 Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Mon, 18 Mar 2024 17:53:01 +0800 Subject: [PATCH 17/32] chore(runtime-vapor): remove unecessary code --- packages/runtime-vapor/src/apiCreateComponent.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/runtime-vapor/src/apiCreateComponent.ts b/packages/runtime-vapor/src/apiCreateComponent.ts index d1d51bcac..c685b3557 100644 --- a/packages/runtime-vapor/src/apiCreateComponent.ts +++ b/packages/runtime-vapor/src/apiCreateComponent.ts @@ -3,7 +3,7 @@ import { createComponentInstance, currentInstance, } from './component' -import { type WithAttrsNode, setupComponent, withAttrsKey } from './apiRender' +import { setupComponent } from './apiRender' import type { RawProps } from './componentProps' export function createComponent( @@ -13,9 +13,6 @@ export function createComponent( ) { const current = currentInstance! const instance = createComponentInstance(comp, rawProps) - if (instance.block && withAttrs && !(withAttrsKey in instance.block)) { - ;(instance.block as WithAttrsNode)[withAttrsKey] = instance.uid - } setupComponent(instance, withAttrs) // register sub-component with current component for lifecycle management From 35499bd2515aaa16682661fd10753e6713416691 Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Mon, 18 Mar 2024 18:00:26 +0800 Subject: [PATCH 18/32] feat(runtime-vapor): should not withAttrs when component set inheritAttrs false --- packages/runtime-vapor/src/helpers/withAttrs.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/runtime-vapor/src/helpers/withAttrs.ts b/packages/runtime-vapor/src/helpers/withAttrs.ts index 6076858e1..e259a07aa 100644 --- a/packages/runtime-vapor/src/helpers/withAttrs.ts +++ b/packages/runtime-vapor/src/helpers/withAttrs.ts @@ -3,6 +3,7 @@ import type { RawProps } from '../componentProps' export function withAttrs(props: RawProps): RawProps { const instance = currentInstance! + if (instance.component.inheritAttrs === false) return props if (!props) return [() => instance.attrs] if (Array.isArray(props)) { return [() => instance.attrs, ...props] From a474ac4a5b177687dd43af5110dcff353b736868 Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Mon, 18 Mar 2024 21:12:55 +0800 Subject: [PATCH 19/32] feat(runtim-vapor, compiler-vapor): simplify implement --- .../src/generators/component.ts | 4 +-- .../__tests__/componentAttrs.spec.ts | 6 ++-- .../runtime-vapor/src/apiCreateComponent.ts | 10 +++++-- packages/runtime-vapor/src/apiRender.ts | 23 +++------------ packages/runtime-vapor/src/componentAttrs.ts | 29 +++++++++++++++++-- packages/runtime-vapor/src/index.ts | 1 - 6 files changed, 42 insertions(+), 31 deletions(-) diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index fc4185842..d264e7e57 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -29,8 +29,8 @@ export function genCreateComponent( const singleRoot = children.length === 1 let props = genProps() - if (singleRoot) { - props = genCall(vaporHelper('withAttrs'), props) + if (!props && singleRoot) { + props = ['null'] } return [ diff --git a/packages/runtime-vapor/__tests__/componentAttrs.spec.ts b/packages/runtime-vapor/__tests__/componentAttrs.spec.ts index e197fd1ff..6919d684c 100644 --- a/packages/runtime-vapor/__tests__/componentAttrs.spec.ts +++ b/packages/runtime-vapor/__tests__/componentAttrs.spec.ts @@ -6,9 +6,7 @@ import { setText, template, watchEffect, - withAttrs, } from '../src' -import { WithAttrsNode } from '../src/apiRender' import { setCurrentInstance } from '../src/component' import { makeRender } from './_utils' @@ -116,9 +114,9 @@ describe('attribute fallthrough', () => { render() { const n0 = createComponent( Grandson, - withAttrs({ + { 'custom-attr': () => 'custom-attr', - }), + }, true, ) return n0 diff --git a/packages/runtime-vapor/src/apiCreateComponent.ts b/packages/runtime-vapor/src/apiCreateComponent.ts index c685b3557..0cb3a26f7 100644 --- a/packages/runtime-vapor/src/apiCreateComponent.ts +++ b/packages/runtime-vapor/src/apiCreateComponent.ts @@ -5,15 +5,19 @@ import { } from './component' import { setupComponent } from './apiRender' import type { RawProps } from './componentProps' +import { withAttrs } from './helpers/withAttrs' export function createComponent( comp: Component, rawProps: RawProps = null, - withAttrs: boolean = false, + singleRoot: boolean = false, ) { const current = currentInstance! - const instance = createComponentInstance(comp, rawProps) - setupComponent(instance, withAttrs) + const instance = createComponentInstance( + comp, + singleRoot && comp.inheritAttrs !== false ? withAttrs(rawProps) : rawProps, + ) + setupComponent(instance, singleRoot) // register sub-component with current component for lifecycle management current.comps.add(instance) diff --git a/packages/runtime-vapor/src/apiRender.ts b/packages/runtime-vapor/src/apiRender.ts index c8f54f713..26ac12186 100644 --- a/packages/runtime-vapor/src/apiRender.ts +++ b/packages/runtime-vapor/src/apiRender.ts @@ -1,12 +1,8 @@ import { isArray, isFunction, isObject } from '@vue/shared' import { type ComponentInternalInstance, setCurrentInstance } from './component' import { insert, querySelector, remove } from './dom/element' -import { - createVaporPreScheduler, - flushPostFlushCbs, - queuePostRenderEffect, -} from './scheduler' -import { baseWatch, proxyRefs } from '@vue/reactivity' +import { flushPostFlushCbs, queuePostRenderEffect } from './scheduler' +import { proxyRefs } from '@vue/reactivity' import { invokeLifecycle } from './componentLifecycle' import { VaporLifecycleHooks } from './apiLifecycle' import { fallThroughAttrs } from './componentAttrs' @@ -26,7 +22,7 @@ export type WithAttrsNode = Node & { export function setupComponent( instance: ComponentInternalInstance, - withAttrs: boolean = false, + singleRoot: boolean = false, ): void { const reset = setCurrentInstance(instance) instance.scope.run(() => { @@ -60,18 +56,7 @@ export function setupComponent( block = [] } instance.block = block - if ( - !instance.component.inheritAttrs && - withAttrs && - !(withAttrsKey in block) - ) { - ;(block as WithAttrsNode)[withAttrsKey] = instance.uid - } - if (instance.component.inheritAttrs !== false) { - baseWatch(() => fallThroughAttrs(instance), undefined, { - scheduler: createVaporPreScheduler(instance), - }) - } + fallThroughAttrs(instance, singleRoot) return block }) reset() diff --git a/packages/runtime-vapor/src/componentAttrs.ts b/packages/runtime-vapor/src/componentAttrs.ts index 7121c2956..8a139c6e7 100644 --- a/packages/runtime-vapor/src/componentAttrs.ts +++ b/packages/runtime-vapor/src/componentAttrs.ts @@ -1,8 +1,10 @@ import { camelize, isFunction } from '@vue/shared' import type { ComponentInternalInstance } from './component' import { isEmitListener } from './componentEmits' -import { type Block, withAttrsKey } from './apiRender' +import { type Block, type WithAttrsNode, withAttrsKey } from './apiRender' import { setDynamicProp } from './dom/prop' +import { baseWatch } from '@vue/reactivity' +import { createVaporPreScheduler } from './scheduler' export function patchAttrs(instance: ComponentInternalInstance) { const attrs = instance.attrs @@ -45,7 +47,30 @@ export function patchAttrs(instance: ComponentInternalInstance) { } } -export function fallThroughAttrs(instance: ComponentInternalInstance) { +export function fallThroughAttrs( + instance: ComponentInternalInstance, + singleRoot: boolean, +) { + const { + block, + component: { inheritAttrs }, + uid, + } = instance + if (singleRoot && inheritAttrs !== false && !(withAttrsKey in block!)) { + ;(block as WithAttrsNode)[withAttrsKey] = uid + } + if ( + inheritAttrs !== false && + withAttrsKey in block! && + block[withAttrsKey] === uid + ) { + baseWatch(() => doFallThroughAttrs(instance), undefined, { + scheduler: createVaporPreScheduler(instance), + }) + } +} + +export function doFallThroughAttrs(instance: ComponentInternalInstance) { const attrs = instance.attrs const block = getFallThroughNode(instance.block!, instance.uid) if (!block) return diff --git a/packages/runtime-vapor/src/index.ts b/packages/runtime-vapor/src/index.ts index a730e15c2..b5687fc1a 100644 --- a/packages/runtime-vapor/src/index.ts +++ b/packages/runtime-vapor/src/index.ts @@ -111,7 +111,6 @@ export { createFor } from './apiCreateFor' export { createComponent } from './apiCreateComponent' export { resolveComponent, resolveDirective } from './helpers/resolveAssets' -export { withAttrs } from './helpers/withAttrs' // **Internal** DOM-only runtime directive helpers export { From 6044bf7ba284c0cadb3f591e6d60684e29ce724a Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Mon, 18 Mar 2024 21:32:05 +0800 Subject: [PATCH 20/32] test(compiler-vapor): add generator component test case & snapshot --- .../__snapshots__/component.spec.ts.snap | 32 +++++++++++++++++++ .../__tests__/generators/component.spec.ts | 18 +++++++++++ 2 files changed, 50 insertions(+) create mode 100644 packages/compiler-vapor/__tests__/generators/__snapshots__/component.spec.ts.snap create mode 100644 packages/compiler-vapor/__tests__/generators/component.spec.ts diff --git a/packages/compiler-vapor/__tests__/generators/__snapshots__/component.spec.ts.snap b/packages/compiler-vapor/__tests__/generators/__snapshots__/component.spec.ts.snap new file mode 100644 index 000000000..b637fb064 --- /dev/null +++ b/packages/compiler-vapor/__tests__/generators/__snapshots__/component.spec.ts.snap @@ -0,0 +1,32 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`generate component > generate multi root component 1`] = ` +"import { resolveComponent as _resolveComponent, createComponent as _createComponent, template as _template } from 'vue/vapor'; +const t0 = _template("123") + +export function render(_ctx) { + const n1 = t0() + const n0 = _createComponent(_resolveComponent("Comp")) + return [n0, n1] +}" +`; + +exports[`generate component > generate single root component (with props) 1`] = ` +"import { resolveComponent as _resolveComponent, createComponent as _createComponent } from 'vue/vapor'; + +export function render(_ctx) { + const n0 = _createComponent(_resolveComponent("Comp"), [{ + foo: () => (foo) + }], true) + return n0 +}" +`; + +exports[`generate component > generate single root component (without props) 1`] = ` +"import { resolveComponent as _resolveComponent, createComponent as _createComponent } from 'vue/vapor'; + +export function render(_ctx) { + const n0 = _createComponent(_resolveComponent("Comp"), null, true) + return n0 +}" +`; diff --git a/packages/compiler-vapor/__tests__/generators/component.spec.ts b/packages/compiler-vapor/__tests__/generators/component.spec.ts new file mode 100644 index 000000000..77ac0936a --- /dev/null +++ b/packages/compiler-vapor/__tests__/generators/component.spec.ts @@ -0,0 +1,18 @@ +import { compile } from '@vue/compiler-vapor' + +describe('generate component', () => { + test('generate single root component (without props)', () => { + const { code } = compile(``) + expect(code).toMatchSnapshot() + }) + + test('generate single root component (with props)', () => { + const { code } = compile(``) + expect(code).toMatchSnapshot() + }) + + test('generate multi root component', () => { + const { code } = compile(`123`) + expect(code).toMatchSnapshot() + }) +}) From c30a618feab3d40afd18edae059d47d66d1caa53 Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Mon, 18 Mar 2024 22:44:42 +0800 Subject: [PATCH 21/32] feat(runtime-vapor): simplify implement --- .../runtime-vapor/src/apiCreateComponent.ts | 4 ++-- packages/runtime-vapor/src/componentAttrs.ts | 22 +++++++++++++------ .../runtime-vapor/src/helpers/withAttrs.ts | 12 ---------- 3 files changed, 17 insertions(+), 21 deletions(-) delete mode 100644 packages/runtime-vapor/src/helpers/withAttrs.ts diff --git a/packages/runtime-vapor/src/apiCreateComponent.ts b/packages/runtime-vapor/src/apiCreateComponent.ts index 0cb3a26f7..3f312a859 100644 --- a/packages/runtime-vapor/src/apiCreateComponent.ts +++ b/packages/runtime-vapor/src/apiCreateComponent.ts @@ -5,7 +5,7 @@ import { } from './component' import { setupComponent } from './apiRender' import type { RawProps } from './componentProps' -import { withAttrs } from './helpers/withAttrs' +import { withAttrs } from './componentAttrs' export function createComponent( comp: Component, @@ -15,7 +15,7 @@ export function createComponent( const current = currentInstance! const instance = createComponentInstance( comp, - singleRoot && comp.inheritAttrs !== false ? withAttrs(rawProps) : rawProps, + singleRoot ? withAttrs(rawProps) : rawProps, ) setupComponent(instance, singleRoot) diff --git a/packages/runtime-vapor/src/componentAttrs.ts b/packages/runtime-vapor/src/componentAttrs.ts index 8a139c6e7..954edc8a4 100644 --- a/packages/runtime-vapor/src/componentAttrs.ts +++ b/packages/runtime-vapor/src/componentAttrs.ts @@ -1,10 +1,11 @@ import { camelize, isFunction } from '@vue/shared' -import type { ComponentInternalInstance } from './component' +import { type ComponentInternalInstance, currentInstance } from './component' import { isEmitListener } from './componentEmits' import { type Block, type WithAttrsNode, withAttrsKey } from './apiRender' import { setDynamicProp } from './dom/prop' import { baseWatch } from '@vue/reactivity' import { createVaporPreScheduler } from './scheduler' +import type { RawProps } from './componentProps' export function patchAttrs(instance: ComponentInternalInstance) { const attrs = instance.attrs @@ -47,6 +48,16 @@ export function patchAttrs(instance: ComponentInternalInstance) { } } +export function withAttrs(props: RawProps): RawProps { + const instance = currentInstance! + if (instance.component.inheritAttrs === false) return props + if (!props) return [() => instance.attrs] + if (Array.isArray(props)) { + return [() => instance.attrs, ...props] + } + return [() => instance.attrs, props] +} + export function fallThroughAttrs( instance: ComponentInternalInstance, singleRoot: boolean, @@ -56,14 +67,11 @@ export function fallThroughAttrs( component: { inheritAttrs }, uid, } = instance - if (singleRoot && inheritAttrs !== false && !(withAttrsKey in block!)) { + if (inheritAttrs === false) return + if (singleRoot && !(withAttrsKey in block!)) { ;(block as WithAttrsNode)[withAttrsKey] = uid } - if ( - inheritAttrs !== false && - withAttrsKey in block! && - block[withAttrsKey] === uid - ) { + if (withAttrsKey in block! && block[withAttrsKey] === uid) { baseWatch(() => doFallThroughAttrs(instance), undefined, { scheduler: createVaporPreScheduler(instance), }) diff --git a/packages/runtime-vapor/src/helpers/withAttrs.ts b/packages/runtime-vapor/src/helpers/withAttrs.ts deleted file mode 100644 index e259a07aa..000000000 --- a/packages/runtime-vapor/src/helpers/withAttrs.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { currentInstance } from '../component' -import type { RawProps } from '../componentProps' - -export function withAttrs(props: RawProps): RawProps { - const instance = currentInstance! - if (instance.component.inheritAttrs === false) return props - if (!props) return [() => instance.attrs] - if (Array.isArray(props)) { - return [() => instance.attrs, ...props] - } - return [() => instance.attrs, props] -} From c085cda553a51e5308c2068699fa1f23bdb2ddec Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Mon, 18 Mar 2024 23:11:25 +0800 Subject: [PATCH 22/32] feat(runtime-vapor): simplify implement --- packages/compiler-vapor/src/generators/component.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index d264e7e57..0ed010e68 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -28,10 +28,7 @@ export function genCreateComponent( : [oper.tag] const singleRoot = children.length === 1 - let props = genProps() - if (!props && singleRoot) { - props = ['null'] - } + const props = genProps() return [ NEWLINE, @@ -39,7 +36,7 @@ export function genCreateComponent( ...genCall( vaporHelper('createComponent'), tag, - props, + props || (singleRoot ? ['null'] : undefined), singleRoot ? 'true' : undefined, ), ] From 0ea395c4643a947ccb3cdcc487e277cceb90382b 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?= Date: Mon, 18 Mar 2024 23:23:03 +0800 Subject: [PATCH 23/32] refactor: optimize bundle size --- packages/compiler-vapor/src/generators/component.ts | 2 +- packages/runtime-vapor/src/apiCreateComponent.ts | 4 ++-- packages/runtime-vapor/src/componentAttrs.ts | 11 ++++++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index 0ed010e68..13c7b62f2 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -36,7 +36,7 @@ export function genCreateComponent( ...genCall( vaporHelper('createComponent'), tag, - props || (singleRoot ? ['null'] : undefined), + props || (singleRoot ? 'null' : false), singleRoot ? 'true' : undefined, ), ] diff --git a/packages/runtime-vapor/src/apiCreateComponent.ts b/packages/runtime-vapor/src/apiCreateComponent.ts index 3f312a859..4b76147f9 100644 --- a/packages/runtime-vapor/src/apiCreateComponent.ts +++ b/packages/runtime-vapor/src/apiCreateComponent.ts @@ -4,12 +4,12 @@ import { currentInstance, } from './component' import { setupComponent } from './apiRender' -import type { RawProps } from './componentProps' +import type { NormalizedRawProps } from './componentProps' import { withAttrs } from './componentAttrs' export function createComponent( comp: Component, - rawProps: RawProps = null, + rawProps: NormalizedRawProps | null = null, singleRoot: boolean = false, ) { const current = currentInstance! diff --git a/packages/runtime-vapor/src/componentAttrs.ts b/packages/runtime-vapor/src/componentAttrs.ts index 954edc8a4..e4260a009 100644 --- a/packages/runtime-vapor/src/componentAttrs.ts +++ b/packages/runtime-vapor/src/componentAttrs.ts @@ -1,4 +1,4 @@ -import { camelize, isFunction } from '@vue/shared' +import { camelize, isArray, isFunction } from '@vue/shared' import { type ComponentInternalInstance, currentInstance } from './component' import { isEmitListener } from './componentEmits' import { type Block, type WithAttrsNode, withAttrsKey } from './apiRender' @@ -51,11 +51,12 @@ export function patchAttrs(instance: ComponentInternalInstance) { export function withAttrs(props: RawProps): RawProps { const instance = currentInstance! if (instance.component.inheritAttrs === false) return props - if (!props) return [() => instance.attrs] - if (Array.isArray(props)) { - return [() => instance.attrs, ...props] + const attrsGetter = () => instance.attrs + if (!props) return [attrsGetter] + if (isArray(props)) { + return [attrsGetter, ...props] } - return [() => instance.attrs, props] + return [attrsGetter, props] } export function fallThroughAttrs( From b5879d34815c1eb5324c8db68e3d417882d2b97f 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?= Date: Mon, 18 Mar 2024 23:52:41 +0800 Subject: [PATCH 24/32] refactor: check if component --- .../runtime-vapor/src/apiCreateComponent.ts | 2 +- packages/runtime-vapor/src/apiRender.ts | 15 ++++----- packages/runtime-vapor/src/component.ts | 4 +++ packages/runtime-vapor/src/componentAttrs.ts | 31 +++---------------- packages/runtime-vapor/src/dom/element.ts | 3 ++ 5 files changed, 20 insertions(+), 35 deletions(-) diff --git a/packages/runtime-vapor/src/apiCreateComponent.ts b/packages/runtime-vapor/src/apiCreateComponent.ts index 4b76147f9..dad9bdafd 100644 --- a/packages/runtime-vapor/src/apiCreateComponent.ts +++ b/packages/runtime-vapor/src/apiCreateComponent.ts @@ -22,5 +22,5 @@ export function createComponent( // register sub-component with current component for lifecycle management current.comps.add(instance) - return instance.block + return instance } diff --git a/packages/runtime-vapor/src/apiRender.ts b/packages/runtime-vapor/src/apiRender.ts index 26ac12186..49802b93e 100644 --- a/packages/runtime-vapor/src/apiRender.ts +++ b/packages/runtime-vapor/src/apiRender.ts @@ -1,5 +1,9 @@ import { isArray, isFunction, isObject } from '@vue/shared' -import { type ComponentInternalInstance, setCurrentInstance } from './component' +import { + type ComponentInternalInstance, + componentKey, + setCurrentInstance, +} from './component' import { insert, querySelector, remove } from './dom/element' import { flushPostFlushCbs, queuePostRenderEffect } from './scheduler' import { proxyRefs } from '@vue/reactivity' @@ -8,17 +12,13 @@ import { VaporLifecycleHooks } from './apiLifecycle' import { fallThroughAttrs } from './componentAttrs' export const fragmentKey = Symbol(__DEV__ ? `fragmentKey` : ``) -export const withAttrsKey = Symbol(__DEV__ ? `withAttrsKey` : ``) -export type Block = Node | WithAttrsNode | Fragment | Block[] +export type Block = Node | Fragment | ComponentInternalInstance | Block[] export type Fragment = { nodes: Block anchor?: Node [fragmentKey]: true } -export type WithAttrsNode = Node & { - [withAttrsKey]: number -} export function setupComponent( instance: ComponentInternalInstance, @@ -38,7 +38,8 @@ export function setupComponent( stateOrNode && (stateOrNode instanceof Node || isArray(stateOrNode) || - (stateOrNode as any)[fragmentKey]) + (stateOrNode as any)[fragmentKey] || + (stateOrNode as any)[componentKey]) ) { block = stateOrNode as Block } else if (isObject(stateOrNode)) { diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 2efd46d3b..79891c744 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -37,7 +37,10 @@ export interface ObjectComponent { type LifecycleHook = TFn[] | null +export const componentKey = Symbol(__DEV__ ? `componentKey` : ``) + export interface ComponentInternalInstance { + [componentKey]: true uid: number vapor: true @@ -144,6 +147,7 @@ export function createComponentInstance( rawProps: RawProps | null, ): ComponentInternalInstance { const instance: ComponentInternalInstance = { + [componentKey]: true, uid: uid++, vapor: true, diff --git a/packages/runtime-vapor/src/componentAttrs.ts b/packages/runtime-vapor/src/componentAttrs.ts index e4260a009..7d1282118 100644 --- a/packages/runtime-vapor/src/componentAttrs.ts +++ b/packages/runtime-vapor/src/componentAttrs.ts @@ -1,11 +1,9 @@ import { camelize, isArray, isFunction } from '@vue/shared' import { type ComponentInternalInstance, currentInstance } from './component' import { isEmitListener } from './componentEmits' -import { type Block, type WithAttrsNode, withAttrsKey } from './apiRender' -import { setDynamicProp } from './dom/prop' -import { baseWatch } from '@vue/reactivity' -import { createVaporPreScheduler } from './scheduler' +import { setDynamicProps } from './dom/prop' import type { RawProps } from './componentProps' +import { renderEffect } from './renderEffect' export function patchAttrs(instance: ComponentInternalInstance) { const attrs = instance.attrs @@ -66,31 +64,10 @@ export function fallThroughAttrs( const { block, component: { inheritAttrs }, - uid, } = instance if (inheritAttrs === false) return - if (singleRoot && !(withAttrsKey in block!)) { - ;(block as WithAttrsNode)[withAttrsKey] = uid - } - if (withAttrsKey in block! && block[withAttrsKey] === uid) { - baseWatch(() => doFallThroughAttrs(instance), undefined, { - scheduler: createVaporPreScheduler(instance), - }) - } -} -export function doFallThroughAttrs(instance: ComponentInternalInstance) { - const attrs = instance.attrs - const block = getFallThroughNode(instance.block!, instance.uid) - if (!block) return - for (const key in attrs) { - if (block instanceof Element) { - setDynamicProp(block, key, attrs[key]) - } + if (singleRoot && block instanceof Element) { + renderEffect(() => setDynamicProps(block, instance.attrs)) } } - -function getFallThroughNode(block: Block, id: number) { - if (withAttrsKey in block && block[withAttrsKey] === id) return block - return null -} diff --git a/packages/runtime-vapor/src/dom/element.ts b/packages/runtime-vapor/src/dom/element.ts index 63c515d4a..d7e450871 100644 --- a/packages/runtime-vapor/src/dom/element.ts +++ b/packages/runtime-vapor/src/dom/element.ts @@ -1,5 +1,6 @@ import { isArray, toDisplayString } from '@vue/shared' import type { Block } from '../apiRender' +import { componentKey } from '../component' /*! #__NO_SIDE_EFFECTS__ */ export function normalizeBlock(block: Block): Node[] { @@ -8,6 +9,8 @@ export function normalizeBlock(block: Block): Node[] { nodes.push(block) } else if (isArray(block)) { block.forEach(child => nodes.push(...normalizeBlock(child))) + } else if (componentKey in block) { + nodes.push(...normalizeBlock(block.block!)) } else if (block) { nodes.push(...normalizeBlock(block.nodes)) block.anchor && nodes.push(block.anchor) From 322f0ba6d8ee52106a2509ecf8d8ef6fb2af6797 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?= Date: Mon, 18 Mar 2024 23:54:22 +0800 Subject: [PATCH 25/32] refactor: remove type casting --- packages/runtime-vapor/src/apiRender.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/runtime-vapor/src/apiRender.ts b/packages/runtime-vapor/src/apiRender.ts index 49802b93e..ec80d1858 100644 --- a/packages/runtime-vapor/src/apiRender.ts +++ b/packages/runtime-vapor/src/apiRender.ts @@ -38,10 +38,10 @@ export function setupComponent( stateOrNode && (stateOrNode instanceof Node || isArray(stateOrNode) || - (stateOrNode as any)[fragmentKey] || - (stateOrNode as any)[componentKey]) + fragmentKey in stateOrNode || + componentKey in stateOrNode) ) { - block = stateOrNode as Block + block = stateOrNode } else if (isObject(stateOrNode)) { instance.setupState = proxyRefs(stateOrNode) } From 34c1937c9fabb3545371e1e24222061cee5145b9 Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Mon, 18 Mar 2024 23:56:28 +0800 Subject: [PATCH 26/32] feat(compiler-vapor): optimize single root implemention --- .../__snapshots__/component.spec.ts.snap | 12 ++++++ .../__tests__/generators/component.spec.ts | 5 +++ .../src/generators/component.ts | 9 +---- packages/compiler-vapor/src/ir.ts | 1 + .../src/transforms/transformElement.ts | 21 +++++++--- .../__tests__/componentAttrs.spec.ts | 38 +++++++++++-------- 6 files changed, 59 insertions(+), 27 deletions(-) diff --git a/packages/compiler-vapor/__tests__/generators/__snapshots__/component.spec.ts.snap b/packages/compiler-vapor/__tests__/generators/__snapshots__/component.spec.ts.snap index b637fb064..999526e70 100644 --- a/packages/compiler-vapor/__tests__/generators/__snapshots__/component.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/generators/__snapshots__/component.spec.ts.snap @@ -30,3 +30,15 @@ export function render(_ctx) { return n0 }" `; + +exports[`generate component > should not generate withAttrs if component is not the root of the template 1`] = ` +"import { resolveComponent as _resolveComponent, createComponent as _createComponent, insert as _insert, template as _template } from 'vue/vapor'; +const t0 = _template("
") + +export function render(_ctx) { + const n1 = t0() + const n0 = _createComponent(_resolveComponent("Comp")) + _insert(n0, n1) + return n1 +}" +`; diff --git a/packages/compiler-vapor/__tests__/generators/component.spec.ts b/packages/compiler-vapor/__tests__/generators/component.spec.ts index 77ac0936a..9d6ded902 100644 --- a/packages/compiler-vapor/__tests__/generators/component.spec.ts +++ b/packages/compiler-vapor/__tests__/generators/component.spec.ts @@ -15,4 +15,9 @@ describe('generate component', () => { const { code } = compile(`123`) expect(code).toMatchSnapshot() }) + + test('should not generate withAttrs if component is not the root of the template', () => { + const { code } = compile(`
`) + expect(code).toMatchSnapshot() + }) }) diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index 13c7b62f2..3155cc9a7 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -16,18 +16,13 @@ export function genCreateComponent( oper: CreateComponentIRNode, context: CodegenContext, ): CodeFragment[] { - const { - vaporHelper, - ir: { - node: { children }, - }, - } = context + const { vaporHelper } = context const tag = oper.resolve ? genCall(vaporHelper('resolveComponent'), JSON.stringify(oper.tag)) : [oper.tag] - const singleRoot = children.length === 1 + const singleRoot = oper.isSingleRoot const props = genProps() return [ diff --git a/packages/compiler-vapor/src/ir.ts b/packages/compiler-vapor/src/ir.ts index 848e98014..3127e2412 100644 --- a/packages/compiler-vapor/src/ir.ts +++ b/packages/compiler-vapor/src/ir.ts @@ -182,6 +182,7 @@ export interface CreateComponentIRNode extends BaseIRNode { // TODO slots resolve: boolean + isSingleRoot: boolean } export type IRNode = OperationNode | RootIRNode diff --git a/packages/compiler-vapor/src/transforms/transformElement.ts b/packages/compiler-vapor/src/transforms/transformElement.ts index 5e80749e0..b980bef50 100644 --- a/packages/compiler-vapor/src/transforms/transformElement.ts +++ b/packages/compiler-vapor/src/transforms/transformElement.ts @@ -1,5 +1,6 @@ import { type AttributeNode, + type ComponentNode, type ElementNode, ElementTypes, ErrorCodes, @@ -48,11 +49,16 @@ export const transformElement: NodeTransform = (node, context) => { context as TransformContext, ) - ;(isComponent ? transformComponentElement : transformNativeElement)( - tag, - propsResult, - context, - ) + if (isComponent) { + transformComponentElement( + tag, + propsResult, + context, + node as ComponentNode, + ) + } else { + transformNativeElement(tag, propsResult, context) + } } } @@ -60,10 +66,14 @@ function transformComponentElement( tag: string, propsResult: PropsResult, context: TransformContext, + node: ComponentNode, ) { const { bindingMetadata } = context.options const resolve = !bindingMetadata[tag] context.dynamic.flags |= DynamicFlag.NON_TEMPLATE | DynamicFlag.INSERT + const isSingleRoot = + context.root.node.children.length === 1 && + context.root.node.children[0] === node context.registerOperation({ type: IRNodeTypes.CREATE_COMPONENT_NODE, @@ -71,6 +81,7 @@ function transformComponentElement( tag, props: propsResult[0] ? propsResult[1] : [propsResult[1]], resolve, + isSingleRoot, }) } diff --git a/packages/runtime-vapor/__tests__/componentAttrs.spec.ts b/packages/runtime-vapor/__tests__/componentAttrs.spec.ts index 6919d684c..8a1970eec 100644 --- a/packages/runtime-vapor/__tests__/componentAttrs.spec.ts +++ b/packages/runtime-vapor/__tests__/componentAttrs.spec.ts @@ -34,10 +34,12 @@ describe('attribute fallthrough', () => { render(_ctx: Record) { return createComponent( Child, - { - foo: () => _ctx.foo, - id: () => _ctx.id, - }, + [ + { + foo: () => _ctx.foo, + id: () => _ctx.id, + }, + ], true, ) }, @@ -77,10 +79,12 @@ describe('attribute fallthrough', () => { render(_ctx: Record) { return createComponent( Child, - { - foo: () => _ctx.foo, - id: () => _ctx.id, - }, + [ + { + foo: () => _ctx.foo, + id: () => _ctx.id, + }, + ], true, ) }, @@ -114,9 +118,11 @@ describe('attribute fallthrough', () => { render() { const n0 = createComponent( Grandson, - { - 'custom-attr': () => 'custom-attr', - }, + [ + { + 'custom-attr': () => 'custom-attr', + }, + ], true, ) return n0 @@ -132,10 +138,12 @@ describe('attribute fallthrough', () => { render(_ctx: Record) { return createComponent( Child, - { - foo: () => _ctx.foo, - id: () => _ctx.id, - }, + [ + { + foo: () => _ctx.foo, + id: () => _ctx.id, + }, + ], true, ) }, From 56ff7c472f0c02e81ce660a229096498062b4a8d 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?= Date: Mon, 18 Mar 2024 23:58:33 +0800 Subject: [PATCH 27/32] fix: type error --- packages/runtime-vapor/src/apiCreateFor.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/runtime-vapor/src/apiCreateFor.ts b/packages/runtime-vapor/src/apiCreateFor.ts index cefcba1a8..b6b37ab52 100644 --- a/packages/runtime-vapor/src/apiCreateFor.ts +++ b/packages/runtime-vapor/src/apiCreateFor.ts @@ -1,9 +1,16 @@ import { type EffectScope, effectScope, isReactive } from '@vue/reactivity' import { isArray, isObject, isString } from '@vue/shared' -import { createComment, createTextNode, insert, remove } from './dom/element' +import { + createComment, + createTextNode, + insert, + normalizeBlock, + remove, +} from './dom/element' import { renderEffect } from './renderEffect' import { type Block, type Fragment, fragmentKey } from './apiRender' import { warn } from './warning' +import { componentKey } from './component' interface ForBlock extends Fragment { scope: EffectScope @@ -343,6 +350,8 @@ function normalizeAnchor(node: Block): Node { return node } else if (isArray(node)) { return normalizeAnchor(node[0]) + } else if (componentKey in node) { + return normalizeAnchor(node.block!) } else { return normalizeAnchor(node.nodes!) } From 47754b7e1929f6532da896fa701377a1566a66b2 Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Tue, 19 Mar 2024 00:03:44 +0800 Subject: [PATCH 28/32] feat(compiler-vapor): perf bundle size --- packages/compiler-vapor/src/generators/component.ts | 6 +++--- packages/compiler-vapor/src/ir.ts | 2 +- packages/compiler-vapor/src/transforms/transformElement.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index 3155cc9a7..7b20af2af 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -22,7 +22,7 @@ export function genCreateComponent( ? genCall(vaporHelper('resolveComponent'), JSON.stringify(oper.tag)) : [oper.tag] - const singleRoot = oper.isSingleRoot + const isRoot = oper.root const props = genProps() return [ @@ -31,8 +31,8 @@ export function genCreateComponent( ...genCall( vaporHelper('createComponent'), tag, - props || (singleRoot ? 'null' : false), - singleRoot ? 'true' : undefined, + props || (isRoot ? 'null' : false), + isRoot ? 'true' : undefined, ), ] diff --git a/packages/compiler-vapor/src/ir.ts b/packages/compiler-vapor/src/ir.ts index 3127e2412..db0ff7e3e 100644 --- a/packages/compiler-vapor/src/ir.ts +++ b/packages/compiler-vapor/src/ir.ts @@ -182,7 +182,7 @@ export interface CreateComponentIRNode extends BaseIRNode { // TODO slots resolve: boolean - isSingleRoot: boolean + root: boolean } export type IRNode = OperationNode | RootIRNode diff --git a/packages/compiler-vapor/src/transforms/transformElement.ts b/packages/compiler-vapor/src/transforms/transformElement.ts index b980bef50..ae6645745 100644 --- a/packages/compiler-vapor/src/transforms/transformElement.ts +++ b/packages/compiler-vapor/src/transforms/transformElement.ts @@ -71,7 +71,7 @@ function transformComponentElement( const { bindingMetadata } = context.options const resolve = !bindingMetadata[tag] context.dynamic.flags |= DynamicFlag.NON_TEMPLATE | DynamicFlag.INSERT - const isSingleRoot = + const root = context.root.node.children.length === 1 && context.root.node.children[0] === node @@ -81,7 +81,7 @@ function transformComponentElement( tag, props: propsResult[0] ? propsResult[1] : [propsResult[1]], resolve, - isSingleRoot, + root, }) } From 5270734a290d51eb4937ebd3c8a93f1b9b2b966a Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Tue, 19 Mar 2024 00:08:57 +0800 Subject: [PATCH 29/32] chore(compiler-vapor, runtime-vapor): bundle size & make lint happy --- packages/compiler-vapor/src/generators/component.ts | 2 +- packages/runtime-vapor/src/apiCreateFor.ts | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index 7b20af2af..a9f57f7d6 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -32,7 +32,7 @@ export function genCreateComponent( vaporHelper('createComponent'), tag, props || (isRoot ? 'null' : false), - isRoot ? 'true' : undefined, + isRoot && 'root', ), ] diff --git a/packages/runtime-vapor/src/apiCreateFor.ts b/packages/runtime-vapor/src/apiCreateFor.ts index b6b37ab52..362ac762c 100644 --- a/packages/runtime-vapor/src/apiCreateFor.ts +++ b/packages/runtime-vapor/src/apiCreateFor.ts @@ -1,12 +1,6 @@ import { type EffectScope, effectScope, isReactive } from '@vue/reactivity' import { isArray, isObject, isString } from '@vue/shared' -import { - createComment, - createTextNode, - insert, - normalizeBlock, - remove, -} from './dom/element' +import { createComment, createTextNode, insert, remove } from './dom/element' import { renderEffect } from './renderEffect' import { type Block, type Fragment, fragmentKey } from './apiRender' import { warn } from './warning' From c06850677353e76896bc8d4fc07e7758264c1b17 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?= Date: Tue, 19 Mar 2024 00:11:23 +0800 Subject: [PATCH 30/32] refactor: simplify --- .../src/generators/component.ts | 2 +- .../src/transforms/transformElement.ts | 19 ++++++------------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index a9f57f7d6..80550bf6f 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -32,7 +32,7 @@ export function genCreateComponent( vaporHelper('createComponent'), tag, props || (isRoot ? 'null' : false), - isRoot && 'root', + isRoot && 'true', ), ] diff --git a/packages/compiler-vapor/src/transforms/transformElement.ts b/packages/compiler-vapor/src/transforms/transformElement.ts index ae6645745..009d03a90 100644 --- a/packages/compiler-vapor/src/transforms/transformElement.ts +++ b/packages/compiler-vapor/src/transforms/transformElement.ts @@ -49,16 +49,11 @@ export const transformElement: NodeTransform = (node, context) => { context as TransformContext, ) - if (isComponent) { - transformComponentElement( - tag, - propsResult, - context, - node as ComponentNode, - ) - } else { - transformNativeElement(tag, propsResult, context) - } + ;(isComponent ? transformComponentElement : transformNativeElement)( + tag, + propsResult, + context, + ) } } @@ -66,14 +61,12 @@ function transformComponentElement( tag: string, propsResult: PropsResult, context: TransformContext, - node: ComponentNode, ) { const { bindingMetadata } = context.options const resolve = !bindingMetadata[tag] context.dynamic.flags |= DynamicFlag.NON_TEMPLATE | DynamicFlag.INSERT const root = - context.root.node.children.length === 1 && - context.root.node.children[0] === node + context.root === context.parent && context.parent.node.children.length === 1 context.registerOperation({ type: IRNodeTypes.CREATE_COMPONENT_NODE, From 61b572cf24f66a9673f5db068ca472f549ee2076 Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Tue, 19 Mar 2024 00:15:12 +0800 Subject: [PATCH 31/32] chore(compiler-vapor): remove unecessary import --- packages/compiler-vapor/src/transforms/transformElement.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/compiler-vapor/src/transforms/transformElement.ts b/packages/compiler-vapor/src/transforms/transformElement.ts index 009d03a90..677ac85aa 100644 --- a/packages/compiler-vapor/src/transforms/transformElement.ts +++ b/packages/compiler-vapor/src/transforms/transformElement.ts @@ -1,6 +1,5 @@ import { type AttributeNode, - type ComponentNode, type ElementNode, ElementTypes, ErrorCodes, From 47f8b9a87c5e5acd30d33851a8cbc244c8576d82 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?= Date: Tue, 19 Mar 2024 00:20:37 +0800 Subject: [PATCH 32/32] test: undo --- .../__tests__/componentProps.spec.ts | 18 +++++++++++------- .../runtime-vapor/src/apiCreateComponent.ts | 4 ++-- packages/runtime-vapor/src/apiRender.ts | 2 +- packages/runtime-vapor/src/componentAttrs.ts | 7 ++----- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/packages/runtime-vapor/__tests__/componentProps.spec.ts b/packages/runtime-vapor/__tests__/componentProps.spec.ts index 87aa15f80..ba85905c6 100644 --- a/packages/runtime-vapor/__tests__/componentProps.spec.ts +++ b/packages/runtime-vapor/__tests__/componentProps.spec.ts @@ -222,7 +222,6 @@ describe('component: props', () => { test('optimized props updates', async () => { const t0 = template('
') const { component: Child } = define({ - inheritAttrs: false, props: ['foo'], render() { const instance = getCurrentInstance()! @@ -239,21 +238,26 @@ describe('component: props', () => { return { foo, id } }, render(_ctx: Record) { - return createComponent(Child, { - foo: () => _ctx.foo, - id: () => _ctx.id, - }) + return createComponent( + Child, + { + foo: () => _ctx.foo, + id: () => _ctx.id, + }, + true, + ) }, }).render() const reset = setCurrentInstance(instance) - expect(host.innerHTML).toBe('
1
') + expect(host.innerHTML).toBe('
1
') foo.value++ await nextTick() - expect(host.innerHTML).toBe('
2
') + expect(host.innerHTML).toBe('
2
') id.value = 'b' await nextTick() + expect(host.innerHTML).toBe('
2
') reset() }) diff --git a/packages/runtime-vapor/src/apiCreateComponent.ts b/packages/runtime-vapor/src/apiCreateComponent.ts index dad9bdafd..133b40fc5 100644 --- a/packages/runtime-vapor/src/apiCreateComponent.ts +++ b/packages/runtime-vapor/src/apiCreateComponent.ts @@ -4,12 +4,12 @@ import { currentInstance, } from './component' import { setupComponent } from './apiRender' -import type { NormalizedRawProps } from './componentProps' +import type { RawProps } from './componentProps' import { withAttrs } from './componentAttrs' export function createComponent( comp: Component, - rawProps: NormalizedRawProps | null = null, + rawProps: RawProps | null = null, singleRoot: boolean = false, ) { const current = currentInstance! diff --git a/packages/runtime-vapor/src/apiRender.ts b/packages/runtime-vapor/src/apiRender.ts index ec80d1858..3ddaeb5cc 100644 --- a/packages/runtime-vapor/src/apiRender.ts +++ b/packages/runtime-vapor/src/apiRender.ts @@ -57,7 +57,7 @@ export function setupComponent( block = [] } instance.block = block - fallThroughAttrs(instance, singleRoot) + if (singleRoot) fallThroughAttrs(instance) return block }) reset() diff --git a/packages/runtime-vapor/src/componentAttrs.ts b/packages/runtime-vapor/src/componentAttrs.ts index 7d1282118..74c4aadf8 100644 --- a/packages/runtime-vapor/src/componentAttrs.ts +++ b/packages/runtime-vapor/src/componentAttrs.ts @@ -57,17 +57,14 @@ export function withAttrs(props: RawProps): RawProps { return [attrsGetter, props] } -export function fallThroughAttrs( - instance: ComponentInternalInstance, - singleRoot: boolean, -) { +export function fallThroughAttrs(instance: ComponentInternalInstance) { const { block, component: { inheritAttrs }, } = instance if (inheritAttrs === false) return - if (singleRoot && block instanceof Element) { + if (block instanceof Element) { renderEffect(() => setDynamicProps(block, instance.attrs)) } }