diff --git a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts
index bf3510a052d..e4ab776a764 100644
--- a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts
+++ b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts
@@ -99,6 +99,16 @@ describe('compiler: element transform', () => {
expect(node.tag).toBe(`$setup["Example"]`)
})
+ test('resolve component from setup bindings & component', () => {
+ const { root, node } = parseWithElementTransform(``, {
+ bindingMetadata: {
+ Example: BindingTypes.SETUP_CONST,
+ },
+ })
+ expect(root.helpers).not.toContain(RESOLVE_COMPONENT)
+ expect(node.tag).toBe(`_resolveSetupReturned("Example", $setup)`)
+ })
+
test('resolve component from setup bindings (inline)', () => {
const { root, node } = parseWithElementTransform(``, {
inline: true,
diff --git a/packages/compiler-core/src/ast.ts b/packages/compiler-core/src/ast.ts
index 8116f532b79..2247f2c758b 100644
--- a/packages/compiler-core/src/ast.ts
+++ b/packages/compiler-core/src/ast.ts
@@ -9,6 +9,7 @@ import {
OPEN_BLOCK,
type RENDER_LIST,
type RENDER_SLOT,
+ RESOLVE_SETUP_RETURNED,
WITH_DIRECTIVES,
type WITH_MEMO,
} from './runtimeHelpers'
@@ -865,6 +866,10 @@ export function getVNodeBlockHelper(
return ssr || isComponent ? CREATE_BLOCK : CREATE_ELEMENT_BLOCK
}
+export function getSetupReturnedHelper() {
+ return RESOLVE_SETUP_RETURNED
+}
+
export function convertToBlock(
node: VNodeCall,
{ helper, removeHelper, inSSR }: TransformContext,
diff --git a/packages/compiler-core/src/codegen.ts b/packages/compiler-core/src/codegen.ts
index 6b6f24b3a30..909fbf42c94 100644
--- a/packages/compiler-core/src/codegen.ts
+++ b/packages/compiler-core/src/codegen.ts
@@ -24,6 +24,7 @@ import {
type TemplateLiteral,
type TextNode,
type VNodeCall,
+ getSetupReturnedHelper,
getVNodeBlockHelper,
getVNodeHelper,
locStub,
@@ -318,6 +319,8 @@ export function generate(
if (!__BROWSER__ && options.bindingMetadata && !options.inline) {
// binding optimization args
args.push('$props', '$setup', '$data', '$options')
+ // Add helper 'getSetupReturnedHelper' for $setup
+ context.helper(getSetupReturnedHelper())
}
const signature =
!__BROWSER__ && options.isTS
diff --git a/packages/compiler-core/src/runtimeHelpers.ts b/packages/compiler-core/src/runtimeHelpers.ts
index 7cf3757b249..3eff4592b6e 100644
--- a/packages/compiler-core/src/runtimeHelpers.ts
+++ b/packages/compiler-core/src/runtimeHelpers.ts
@@ -26,6 +26,9 @@ export const CREATE_STATIC: unique symbol = Symbol(
export const RESOLVE_COMPONENT: unique symbol = Symbol(
__DEV__ ? `resolveComponent` : ``,
)
+export const RESOLVE_SETUP_RETURNED = Symbol(
+ __DEV__ ? `resolveSetupReturned` : ``,
+)
export const RESOLVE_DYNAMIC_COMPONENT: unique symbol = Symbol(
__DEV__ ? `resolveDynamicComponent` : ``,
)
@@ -98,6 +101,7 @@ export const helperNameMap: Record = {
[CREATE_TEXT]: `createTextVNode`,
[CREATE_STATIC]: `createStaticVNode`,
[RESOLVE_COMPONENT]: `resolveComponent`,
+ [RESOLVE_SETUP_RETURNED]: `resolveSetupReturned`,
[RESOLVE_DYNAMIC_COMPONENT]: `resolveDynamicComponent`,
[RESOLVE_DIRECTIVE]: `resolveDirective`,
[RESOLVE_FILTER]: `resolveFilter`,
diff --git a/packages/compiler-core/src/transforms/transformElement.ts b/packages/compiler-core/src/transforms/transformElement.ts
index c917436ea91..471aac038f6 100644
--- a/packages/compiler-core/src/transforms/transformElement.ts
+++ b/packages/compiler-core/src/transforms/transformElement.ts
@@ -21,6 +21,7 @@ import {
createObjectProperty,
createSimpleExpression,
createVNodeCall,
+ getSetupReturnedHelper,
} from '../ast'
import {
PatchFlags,
@@ -344,10 +345,13 @@ function resolveSetupReference(name: string, context: TransformContext) {
checkType(BindingTypes.SETUP_REACTIVE_CONST) ||
checkType(BindingTypes.LITERAL_CONST)
if (fromConst) {
+ const helper = context.helperString
return context.inline
? // in inline mode, const setup bindings (e.g. imports) can be used as-is
fromConst
- : `$setup[${JSON.stringify(fromConst)}]`
+ : `${helper(getSetupReturnedHelper())}(${JSON.stringify(
+ fromConst,
+ )}, $setup)`
}
const fromMaybeRef =
diff --git a/packages/runtime-core/src/helpers/resolveAssets.ts b/packages/runtime-core/src/helpers/resolveAssets.ts
index aa6532c2811..a6dd122e5af 100644
--- a/packages/runtime-core/src/helpers/resolveAssets.ts
+++ b/packages/runtime-core/src/helpers/resolveAssets.ts
@@ -6,7 +6,7 @@ import {
} from '../component'
import { currentRenderingInstance } from '../componentRenderContext'
import type { Directive } from '../directives'
-import { camelize, capitalize, isString } from '@vue/shared'
+import { camelize, capitalize, isLateTag, isString } from '@vue/shared'
import { warn } from '../warning'
import type { VNodeTypes } from '../vnode'
@@ -106,18 +106,26 @@ function resolveAsset(
resolve(instance[type] || (Component as ComponentOptions)[type], name) ||
// global registration
resolve(instance.appContext[type], name)
-
if (!res && maybeSelfReference) {
// fallback to implicit self-reference
return Component
}
- if (__DEV__ && warnMissing && !res) {
- const extra =
- type === COMPONENTS
- ? `\nIf this is a native custom element, make sure to exclude it from ` +
+ if (
+ __DEV__ &&
+ warnMissing &&
+ ((!res && !isLateTag(name)) || (res && isLateTag(name)))
+ ) {
+ let extra = ''
+ if (type === COMPONENTS) {
+ if (isLateTag(name)) {
+ extra = `\nplease do not use built-in tag names as component names.`
+ } else {
+ extra =
+ `\nIf this is a native custom element, make sure to exclude it from ` +
`component resolution via compilerOptions.isCustomElement.`
- : ``
+ }
+ }
warn(`Failed to resolve ${type.slice(0, -1)}: ${name}${extra}`)
}
@@ -138,3 +146,16 @@ function resolve(registry: Record | undefined, name: string) {
registry[capitalize(camelize(name))])
)
}
+
+/**
+ * @private
+ */
+export function resolveSetupReturned(name: string, setupReturn: any) {
+ if (!setupReturn) return name
+ const returnValue = setupReturn[name]
+ if (returnValue && returnValue.__file && isLateTag(name as string)) {
+ const extra = `\nplease do not use built-in tag names as component names.`
+ warn(`Failed to resolve component: ${name},${extra}`)
+ }
+ return returnValue
+}
diff --git a/packages/runtime-core/src/hydrationStrategies.ts b/packages/runtime-core/src/hydrationStrategies.ts
index 51200fc1c34..791ca9e5254 100644
--- a/packages/runtime-core/src/hydrationStrategies.ts
+++ b/packages/runtime-core/src/hydrationStrategies.ts
@@ -26,6 +26,16 @@ export const hydrateOnIdle: HydrationStrategyFactory =
return () => cancelIdleCallback(id)
}
+function elementIsVisibleInViewport(el: Element) {
+ const { top, left, bottom, right } = el.getBoundingClientRect()
+ // eslint-disable-next-line no-restricted-globals
+ const { innerHeight, innerWidth } = window
+ return (
+ ((top > 0 && top < innerHeight) || (bottom > 0 && bottom < innerHeight)) &&
+ ((left > 0 && left < innerWidth) || (right > 0 && right < innerWidth))
+ )
+}
+
export const hydrateOnVisible: HydrationStrategyFactory<
IntersectionObserverInit
> = opts => (hydrate, forEach) => {
@@ -37,7 +47,14 @@ export const hydrateOnVisible: HydrationStrategyFactory<
break
}
}, opts)
- forEach(el => ob.observe(el))
+ forEach(el => {
+ if (elementIsVisibleInViewport(el)) {
+ hydrate()
+ ob.disconnect()
+ return false
+ }
+ ob.observe(el)
+ })
return () => ob.disconnect()
}
@@ -85,14 +102,20 @@ export const hydrateOnInteraction: HydrationStrategyFactory<
return teardown
}
-export function forEachElement(node: Node, cb: (el: Element) => void): void {
+export function forEachElement(
+ node: Node,
+ cb: (el: Element) => void | false,
+): void {
// fragment
if (isComment(node) && node.data === '[') {
let depth = 1
let next = node.nextSibling
while (next) {
if (next.nodeType === DOMNodeTypes.ELEMENT) {
- cb(next as Element)
+ const result = cb(next as Element)
+ if (result === false) {
+ break
+ }
} else if (isComment(next)) {
if (next.data === ']') {
if (--depth === 0) break
diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts
index 7f716b5f4e8..15c915c8919 100644
--- a/packages/runtime-core/src/index.ts
+++ b/packages/runtime-core/src/index.ts
@@ -144,6 +144,7 @@ export {
resolveComponent,
resolveDirective,
resolveDynamicComponent,
+ resolveSetupReturned,
} from './helpers/resolveAssets'
// For integration with runtime compiler
export { registerRuntimeCompiler, isRuntimeOnly } from './component'
diff --git a/packages/shared/src/domTagConfig.ts b/packages/shared/src/domTagConfig.ts
index 7f9d198e569..9084909c4b1 100644
--- a/packages/shared/src/domTagConfig.ts
+++ b/packages/shared/src/domTagConfig.ts
@@ -14,6 +14,8 @@ const HTML_TAGS =
'option,output,progress,select,textarea,details,dialog,menu,' +
'summary,template,blockquote,iframe,tfoot'
+const LATE_ADDED_TAGS = 'search'
+
// https://developer.mozilla.org/en-US/docs/Web/SVG/Element
const SVG_TAGS =
'svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,' +
@@ -62,3 +64,5 @@ export const isMathMLTag: (key: string) => boolean =
*/
export const isVoidTag: (key: string) => boolean =
/*@__PURE__*/ makeMap(VOID_TAGS)
+
+export const isLateTag = /*#__PURE__*/ makeMap(LATE_ADDED_TAGS)