mirror of https://github.com/vuejs/core.git
feat: implement inheritAttrs (#153)
Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
This commit is contained in:
parent
6fc5cfbc65
commit
38e167ceb8
|
@ -0,0 +1,44 @@
|
||||||
|
// 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
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
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("<div></div>")
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const n1 = t0()
|
||||||
|
const n0 = _createComponent(_resolveComponent("Comp"))
|
||||||
|
_insert(n0, n1)
|
||||||
|
return n1
|
||||||
|
}"
|
||||||
|
`;
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { compile } from '@vue/compiler-vapor'
|
||||||
|
|
||||||
|
describe('generate component', () => {
|
||||||
|
test('generate single root component (without props)', () => {
|
||||||
|
const { code } = compile(`<Comp/>`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('generate single root component (with props)', () => {
|
||||||
|
const { code } = compile(`<Comp :foo="foo"/>`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('generate multi root component', () => {
|
||||||
|
const { code } = compile(`<Comp/>123`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should not generate withAttrs if component is not the root of the template', () => {
|
||||||
|
const { code } = compile(`<div><Comp/></div>`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
})
|
|
@ -22,10 +22,18 @@ export function genCreateComponent(
|
||||||
? genCall(vaporHelper('resolveComponent'), JSON.stringify(oper.tag))
|
? genCall(vaporHelper('resolveComponent'), JSON.stringify(oper.tag))
|
||||||
: [oper.tag]
|
: [oper.tag]
|
||||||
|
|
||||||
|
const isRoot = oper.root
|
||||||
|
const props = genProps()
|
||||||
|
|
||||||
return [
|
return [
|
||||||
NEWLINE,
|
NEWLINE,
|
||||||
`const n${oper.id} = `,
|
`const n${oper.id} = `,
|
||||||
...genCall(vaporHelper('createComponent'), tag, genProps()),
|
...genCall(
|
||||||
|
vaporHelper('createComponent'),
|
||||||
|
tag,
|
||||||
|
props || (isRoot ? 'null' : false),
|
||||||
|
isRoot && 'true',
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
function genProps() {
|
function genProps() {
|
||||||
|
|
|
@ -182,6 +182,7 @@ export interface CreateComponentIRNode extends BaseIRNode {
|
||||||
// TODO slots
|
// TODO slots
|
||||||
|
|
||||||
resolve: boolean
|
resolve: boolean
|
||||||
|
root: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IRNode = OperationNode | RootIRNode
|
export type IRNode = OperationNode | RootIRNode
|
||||||
|
|
|
@ -64,6 +64,8 @@ function transformComponentElement(
|
||||||
const { bindingMetadata } = context.options
|
const { bindingMetadata } = context.options
|
||||||
const resolve = !bindingMetadata[tag]
|
const resolve = !bindingMetadata[tag]
|
||||||
context.dynamic.flags |= DynamicFlag.NON_TEMPLATE | DynamicFlag.INSERT
|
context.dynamic.flags |= DynamicFlag.NON_TEMPLATE | DynamicFlag.INSERT
|
||||||
|
const root =
|
||||||
|
context.root === context.parent && context.parent.node.children.length === 1
|
||||||
|
|
||||||
context.registerOperation({
|
context.registerOperation({
|
||||||
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
||||||
|
@ -71,6 +73,7 @@ function transformComponentElement(
|
||||||
tag,
|
tag,
|
||||||
props: propsResult[0] ? propsResult[1] : [propsResult[1]],
|
props: propsResult[0] ? propsResult[1] : [propsResult[1]],
|
||||||
resolve,
|
resolve,
|
||||||
|
root,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
import {
|
||||||
|
createComponent,
|
||||||
|
getCurrentInstance,
|
||||||
|
nextTick,
|
||||||
|
ref,
|
||||||
|
setText,
|
||||||
|
template,
|
||||||
|
watchEffect,
|
||||||
|
} from '../src'
|
||||||
|
import { setCurrentInstance } from '../src/component'
|
||||||
|
import { makeRender } from './_utils'
|
||||||
|
|
||||||
|
const define = makeRender<any>()
|
||||||
|
|
||||||
|
describe('attribute fallthrough', () => {
|
||||||
|
it('should allow attrs to fallthrough', async () => {
|
||||||
|
const t0 = template('<div>')
|
||||||
|
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<string, any>) {
|
||||||
|
return createComponent(
|
||||||
|
Child,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
foo: () => _ctx.foo,
|
||||||
|
id: () => _ctx.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}).render()
|
||||||
|
const reset = setCurrentInstance(instance)
|
||||||
|
expect(host.innerHTML).toBe('<div id="a">1</div>')
|
||||||
|
|
||||||
|
foo.value++
|
||||||
|
await nextTick()
|
||||||
|
expect(host.innerHTML).toBe('<div id="a">2</div>')
|
||||||
|
|
||||||
|
id.value = 'b'
|
||||||
|
await nextTick()
|
||||||
|
expect(host.innerHTML).toBe('<div id="b">2</div>')
|
||||||
|
reset()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not fallthrough if explicitly pass inheritAttrs: false', async () => {
|
||||||
|
const t0 = template('<div>')
|
||||||
|
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<string, any>) {
|
||||||
|
return createComponent(
|
||||||
|
Child,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
foo: () => _ctx.foo,
|
||||||
|
id: () => _ctx.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}).render()
|
||||||
|
const reset = setCurrentInstance(instance)
|
||||||
|
expect(host.innerHTML).toBe('<div>1</div>')
|
||||||
|
|
||||||
|
foo.value++
|
||||||
|
await nextTick()
|
||||||
|
expect(host.innerHTML).toBe('<div>2</div>')
|
||||||
|
|
||||||
|
id.value = 'b'
|
||||||
|
await nextTick()
|
||||||
|
expect(host.innerHTML).toBe('<div>2</div>')
|
||||||
|
reset()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should pass through attrs in nested single root components', async () => {
|
||||||
|
const t0 = template('<div>')
|
||||||
|
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,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
'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<string, any>) {
|
||||||
|
return createComponent(
|
||||||
|
Child,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
foo: () => _ctx.foo,
|
||||||
|
id: () => _ctx.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}).render()
|
||||||
|
const reset = setCurrentInstance(instance)
|
||||||
|
expect(host.innerHTML).toBe('<div foo="1" id="a">1</div>')
|
||||||
|
|
||||||
|
foo.value++
|
||||||
|
await nextTick()
|
||||||
|
expect(host.innerHTML).toBe('<div foo="2" id="a">2</div>')
|
||||||
|
|
||||||
|
id.value = 'b'
|
||||||
|
await nextTick()
|
||||||
|
expect(host.innerHTML).toBe('<div foo="2" id="b">2</div>')
|
||||||
|
reset()
|
||||||
|
})
|
||||||
|
})
|
|
@ -238,24 +238,26 @@ describe('component: props', () => {
|
||||||
return { foo, id }
|
return { foo, id }
|
||||||
},
|
},
|
||||||
render(_ctx: Record<string, any>) {
|
render(_ctx: Record<string, any>) {
|
||||||
return createComponent(Child, {
|
return createComponent(
|
||||||
foo: () => _ctx.foo,
|
Child,
|
||||||
id: () => _ctx.id,
|
{
|
||||||
})
|
foo: () => _ctx.foo,
|
||||||
|
id: () => _ctx.id,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
}).render()
|
}).render()
|
||||||
const reset = setCurrentInstance(instance)
|
const reset = setCurrentInstance(instance)
|
||||||
// expect(host.innerHTML).toBe('<div id="a">1</div>') // TODO: Fallthrough Attributes
|
expect(host.innerHTML).toBe('<div id="a">1</div>')
|
||||||
expect(host.innerHTML).toBe('<div>1</div>')
|
|
||||||
|
|
||||||
foo.value++
|
foo.value++
|
||||||
await nextTick()
|
await nextTick()
|
||||||
// expect(host.innerHTML).toBe('<div id="a">2</div>') // TODO: Fallthrough Attributes
|
expect(host.innerHTML).toBe('<div id="a">2</div>')
|
||||||
expect(host.innerHTML).toBe('<div>2</div>')
|
|
||||||
|
|
||||||
id.value = 'b'
|
id.value = 'b'
|
||||||
await nextTick()
|
await nextTick()
|
||||||
// expect(host.innerHTML).toBe('<div id="b">2</div>') // TODO: Fallthrough Attributes
|
expect(host.innerHTML).toBe('<div id="b">2</div>')
|
||||||
reset()
|
reset()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -441,6 +443,7 @@ describe('component: props', () => {
|
||||||
// #5016
|
// #5016
|
||||||
test('handling attr with undefined value', () => {
|
test('handling attr with undefined value', () => {
|
||||||
const { render, host } = define({
|
const { render, host } = define({
|
||||||
|
inheritAttrs: false,
|
||||||
render() {
|
render() {
|
||||||
const instance = getCurrentInstance()!
|
const instance = getCurrentInstance()!
|
||||||
const t0 = template('<div></div>')
|
const t0 = template('<div></div>')
|
||||||
|
|
|
@ -5,14 +5,22 @@ import {
|
||||||
} from './component'
|
} from './component'
|
||||||
import { setupComponent } from './apiRender'
|
import { setupComponent } from './apiRender'
|
||||||
import type { RawProps } from './componentProps'
|
import type { RawProps } from './componentProps'
|
||||||
|
import { withAttrs } from './componentAttrs'
|
||||||
|
|
||||||
export function createComponent(comp: Component, rawProps: RawProps = null) {
|
export function createComponent(
|
||||||
|
comp: Component,
|
||||||
|
rawProps: RawProps | null = null,
|
||||||
|
singleRoot: boolean = false,
|
||||||
|
) {
|
||||||
const current = currentInstance!
|
const current = currentInstance!
|
||||||
const instance = createComponentInstance(comp, rawProps)
|
const instance = createComponentInstance(
|
||||||
setupComponent(instance)
|
comp,
|
||||||
|
singleRoot ? withAttrs(rawProps) : rawProps,
|
||||||
|
)
|
||||||
|
setupComponent(instance, singleRoot)
|
||||||
|
|
||||||
// register sub-component with current component for lifecycle management
|
// register sub-component with current component for lifecycle management
|
||||||
current.comps.add(instance)
|
current.comps.add(instance)
|
||||||
|
|
||||||
return instance.block
|
return instance
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { createComment, createTextNode, insert, remove } from './dom/element'
|
||||||
import { renderEffect } from './renderEffect'
|
import { renderEffect } from './renderEffect'
|
||||||
import { type Block, type Fragment, fragmentKey } from './apiRender'
|
import { type Block, type Fragment, fragmentKey } from './apiRender'
|
||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
|
import { componentKey } from './component'
|
||||||
|
|
||||||
interface ForBlock extends Fragment {
|
interface ForBlock extends Fragment {
|
||||||
scope: EffectScope
|
scope: EffectScope
|
||||||
|
@ -343,6 +344,8 @@ function normalizeAnchor(node: Block): Node {
|
||||||
return node
|
return node
|
||||||
} else if (isArray(node)) {
|
} else if (isArray(node)) {
|
||||||
return normalizeAnchor(node[0])
|
return normalizeAnchor(node[0])
|
||||||
|
} else if (componentKey in node) {
|
||||||
|
return normalizeAnchor(node.block!)
|
||||||
} else {
|
} else {
|
||||||
return normalizeAnchor(node.nodes!)
|
return normalizeAnchor(node.nodes!)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,29 @@
|
||||||
import { isArray, isFunction, isObject } from '@vue/shared'
|
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 { insert, querySelector, remove } from './dom/element'
|
||||||
import { flushPostFlushCbs, queuePostRenderEffect } from './scheduler'
|
import { flushPostFlushCbs, queuePostRenderEffect } from './scheduler'
|
||||||
import { proxyRefs } from '@vue/reactivity'
|
import { proxyRefs } from '@vue/reactivity'
|
||||||
import { invokeLifecycle } from './componentLifecycle'
|
import { invokeLifecycle } from './componentLifecycle'
|
||||||
import { VaporLifecycleHooks } from './apiLifecycle'
|
import { VaporLifecycleHooks } from './apiLifecycle'
|
||||||
|
import { fallThroughAttrs } from './componentAttrs'
|
||||||
|
|
||||||
export const fragmentKey = Symbol(__DEV__ ? `fragmentKey` : ``)
|
export const fragmentKey = Symbol(__DEV__ ? `fragmentKey` : ``)
|
||||||
|
|
||||||
export type Block = Node | Fragment | Block[]
|
export type Block = Node | Fragment | ComponentInternalInstance | Block[]
|
||||||
export type Fragment = {
|
export type Fragment = {
|
||||||
nodes: Block
|
nodes: Block
|
||||||
anchor?: Node
|
anchor?: Node
|
||||||
[fragmentKey]: true
|
[fragmentKey]: true
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setupComponent(instance: ComponentInternalInstance): void {
|
export function setupComponent(
|
||||||
|
instance: ComponentInternalInstance,
|
||||||
|
singleRoot: boolean = false,
|
||||||
|
): void {
|
||||||
const reset = setCurrentInstance(instance)
|
const reset = setCurrentInstance(instance)
|
||||||
instance.scope.run(() => {
|
instance.scope.run(() => {
|
||||||
const { component, props, emit, attrs } = instance
|
const { component, props, emit, attrs } = instance
|
||||||
|
@ -30,9 +38,10 @@ export function setupComponent(instance: ComponentInternalInstance): void {
|
||||||
stateOrNode &&
|
stateOrNode &&
|
||||||
(stateOrNode instanceof Node ||
|
(stateOrNode instanceof Node ||
|
||||||
isArray(stateOrNode) ||
|
isArray(stateOrNode) ||
|
||||||
(stateOrNode as any)[fragmentKey])
|
fragmentKey in stateOrNode ||
|
||||||
|
componentKey in stateOrNode)
|
||||||
) {
|
) {
|
||||||
block = stateOrNode as Block
|
block = stateOrNode
|
||||||
} else if (isObject(stateOrNode)) {
|
} else if (isObject(stateOrNode)) {
|
||||||
instance.setupState = proxyRefs(stateOrNode)
|
instance.setupState = proxyRefs(stateOrNode)
|
||||||
}
|
}
|
||||||
|
@ -47,7 +56,9 @@ export function setupComponent(instance: ComponentInternalInstance): void {
|
||||||
// TODO: warn no template
|
// TODO: warn no template
|
||||||
block = []
|
block = []
|
||||||
}
|
}
|
||||||
return (instance.block = block)
|
instance.block = block
|
||||||
|
if (singleRoot) fallThroughAttrs(instance)
|
||||||
|
return block
|
||||||
})
|
})
|
||||||
reset()
|
reset()
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ export type FunctionalComponent = SetupFn & Omit<ObjectComponent, 'setup'>
|
||||||
|
|
||||||
export interface ObjectComponent {
|
export interface ObjectComponent {
|
||||||
props?: ComponentPropsOptions
|
props?: ComponentPropsOptions
|
||||||
|
inheritAttrs?: boolean
|
||||||
emits?: EmitsOptions
|
emits?: EmitsOptions
|
||||||
setup?: SetupFn
|
setup?: SetupFn
|
||||||
render?(ctx: any): Block
|
render?(ctx: any): Block
|
||||||
|
@ -36,7 +37,10 @@ export interface ObjectComponent {
|
||||||
|
|
||||||
type LifecycleHook<TFn = Function> = TFn[] | null
|
type LifecycleHook<TFn = Function> = TFn[] | null
|
||||||
|
|
||||||
|
export const componentKey = Symbol(__DEV__ ? `componentKey` : ``)
|
||||||
|
|
||||||
export interface ComponentInternalInstance {
|
export interface ComponentInternalInstance {
|
||||||
|
[componentKey]: true
|
||||||
uid: number
|
uid: number
|
||||||
vapor: true
|
vapor: true
|
||||||
|
|
||||||
|
@ -143,6 +147,7 @@ export function createComponentInstance(
|
||||||
rawProps: RawProps | null,
|
rawProps: RawProps | null,
|
||||||
): ComponentInternalInstance {
|
): ComponentInternalInstance {
|
||||||
const instance: ComponentInternalInstance = {
|
const instance: ComponentInternalInstance = {
|
||||||
|
[componentKey]: true,
|
||||||
uid: uid++,
|
uid: uid++,
|
||||||
vapor: true,
|
vapor: true,
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import { camelize, isFunction } from '@vue/shared'
|
import { camelize, isArray, isFunction } from '@vue/shared'
|
||||||
import type { ComponentInternalInstance } from './component'
|
import { type ComponentInternalInstance, currentInstance } from './component'
|
||||||
import { isEmitListener } from './componentEmits'
|
import { isEmitListener } from './componentEmits'
|
||||||
|
import { setDynamicProps } from './dom/prop'
|
||||||
|
import type { RawProps } from './componentProps'
|
||||||
|
import { renderEffect } from './renderEffect'
|
||||||
|
|
||||||
export function patchAttrs(instance: ComponentInternalInstance) {
|
export function patchAttrs(instance: ComponentInternalInstance) {
|
||||||
const attrs = instance.attrs
|
const attrs = instance.attrs
|
||||||
|
@ -42,3 +45,26 @@ export function patchAttrs(instance: ComponentInternalInstance) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function withAttrs(props: RawProps): RawProps {
|
||||||
|
const instance = currentInstance!
|
||||||
|
if (instance.component.inheritAttrs === false) return props
|
||||||
|
const attrsGetter = () => instance.attrs
|
||||||
|
if (!props) return [attrsGetter]
|
||||||
|
if (isArray(props)) {
|
||||||
|
return [attrsGetter, ...props]
|
||||||
|
}
|
||||||
|
return [attrsGetter, props]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fallThroughAttrs(instance: ComponentInternalInstance) {
|
||||||
|
const {
|
||||||
|
block,
|
||||||
|
component: { inheritAttrs },
|
||||||
|
} = instance
|
||||||
|
if (inheritAttrs === false) return
|
||||||
|
|
||||||
|
if (block instanceof Element) {
|
||||||
|
renderEffect(() => setDynamicProps(block, instance.attrs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { isArray, toDisplayString } from '@vue/shared'
|
import { isArray, toDisplayString } from '@vue/shared'
|
||||||
import type { Block } from '../apiRender'
|
import type { Block } from '../apiRender'
|
||||||
|
import { componentKey } from '../component'
|
||||||
|
|
||||||
/*! #__NO_SIDE_EFFECTS__ */
|
/*! #__NO_SIDE_EFFECTS__ */
|
||||||
export function normalizeBlock(block: Block): Node[] {
|
export function normalizeBlock(block: Block): Node[] {
|
||||||
|
@ -8,6 +9,8 @@ export function normalizeBlock(block: Block): Node[] {
|
||||||
nodes.push(block)
|
nodes.push(block)
|
||||||
} else if (isArray(block)) {
|
} else if (isArray(block)) {
|
||||||
block.forEach(child => nodes.push(...normalizeBlock(child)))
|
block.forEach(child => nodes.push(...normalizeBlock(child)))
|
||||||
|
} else if (componentKey in block) {
|
||||||
|
nodes.push(...normalizeBlock(block.block!))
|
||||||
} else if (block) {
|
} else if (block) {
|
||||||
nodes.push(...normalizeBlock(block.nodes))
|
nodes.push(...normalizeBlock(block.nodes))
|
||||||
block.anchor && nodes.push(block.anchor)
|
block.anchor && nodes.push(block.anchor)
|
||||||
|
|
Loading…
Reference in New Issue