fix(template-ref): fix string template refs inside slots
This commit is contained in:
parent
8cb0b83088
commit
3eab143843
@ -0,0 +1,49 @@
|
||||
import { baseParse as parse } from '../../src/parse'
|
||||
import { transform } from '../../src/transform'
|
||||
import { transformRef } from '../../src/transforms/transformRef'
|
||||
import { ElementNode, NodeTypes } from '../../src/ast'
|
||||
|
||||
function transformWithRef(template: string) {
|
||||
const ast = parse(template)
|
||||
transform(ast, {
|
||||
nodeTransforms: [transformRef]
|
||||
})
|
||||
return ast.children[0] as ElementNode
|
||||
}
|
||||
|
||||
describe('compiler: transform ref', () => {
|
||||
const getExpected = (key: any) => ({
|
||||
type: NodeTypes.DIRECTIVE,
|
||||
name: 'bind',
|
||||
arg: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `ref`
|
||||
},
|
||||
exp: {
|
||||
type: NodeTypes.COMPOUND_EXPRESSION,
|
||||
children: [`[_ctx, `, key, `]`]
|
||||
}
|
||||
})
|
||||
|
||||
test('static', () => {
|
||||
const node = transformWithRef(`<div ref="test"/>`)
|
||||
expect(node.props[0]).toMatchObject(
|
||||
getExpected({
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `test`,
|
||||
isStatic: true
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
test('dynamic', () => {
|
||||
const node = transformWithRef(`<div :ref="test"/>`)
|
||||
expect(node.props[0]).toMatchObject(
|
||||
getExpected({
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `test`,
|
||||
isStatic: false
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
@ -4,6 +4,7 @@ import { transform, NodeTransform, DirectiveTransform } from './transform'
|
||||
import { generate, CodegenResult } from './codegen'
|
||||
import { RootNode } from './ast'
|
||||
import { isString } from '@vue/shared'
|
||||
import { transformRef } from './transforms/transformRef'
|
||||
import { transformIf } from './transforms/vIf'
|
||||
import { transformFor } from './transforms/vFor'
|
||||
import { transformExpression } from './transforms/transformExpression'
|
||||
@ -27,6 +28,7 @@ export function getBaseTransformPreset(
|
||||
): TransformPreset {
|
||||
return [
|
||||
[
|
||||
transformRef,
|
||||
transformOnce,
|
||||
transformIf,
|
||||
transformFor,
|
||||
|
40
packages/compiler-core/src/transforms/transformRef.ts
Normal file
40
packages/compiler-core/src/transforms/transformRef.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { NodeTransform } from '../transform'
|
||||
import {
|
||||
NodeTypes,
|
||||
ElementTypes,
|
||||
createSimpleExpression,
|
||||
createCompoundExpression
|
||||
} from '../ast'
|
||||
import { findProp } from '../utils'
|
||||
|
||||
// Convert ref="foo" to `:ref="[_ctx, 'foo']"` so that the ref contains the
|
||||
// correct owner instance even inside slots.
|
||||
export const transformRef: NodeTransform = node => {
|
||||
if (
|
||||
!(
|
||||
node.type === NodeTypes.ELEMENT &&
|
||||
(node.tagType === ElementTypes.ELEMENT ||
|
||||
node.tagType === ElementTypes.COMPONENT)
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
const ref = findProp(node, 'ref')
|
||||
if (!ref) return
|
||||
const refKey =
|
||||
ref.type === NodeTypes.ATTRIBUTE
|
||||
? ref.value
|
||||
? createSimpleExpression(ref.value.content, true, ref.value.loc)
|
||||
: null
|
||||
: ref.exp
|
||||
if (refKey) {
|
||||
node.props[node.props.indexOf(ref)] = {
|
||||
type: NodeTypes.DIRECTIVE,
|
||||
name: `bind`,
|
||||
arg: createSimpleExpression(`ref`, true, ref.loc),
|
||||
exp: createCompoundExpression([`[_ctx, `, refKey, `]`]),
|
||||
modifiers: [],
|
||||
loc: ref.loc
|
||||
}
|
||||
}
|
||||
}
|
@ -22,7 +22,9 @@ describe('api: template refs', () => {
|
||||
}
|
||||
},
|
||||
render() {
|
||||
return h('div', { ref: 'refKey' })
|
||||
// Note: string refs are compiled into [ctx, key] tuples by the compiler
|
||||
// to ensure correct context.
|
||||
return h('div', { ref: [this, 'refKey'] as any })
|
||||
}
|
||||
}
|
||||
render(h(Comp), root)
|
||||
@ -43,7 +45,7 @@ describe('api: template refs', () => {
|
||||
}
|
||||
},
|
||||
render() {
|
||||
return h('div', { ref: refKey.value })
|
||||
return h('div', { ref: [this, refKey.value] as any })
|
||||
}
|
||||
}
|
||||
render(h(Comp), root)
|
||||
@ -68,7 +70,7 @@ describe('api: template refs', () => {
|
||||
}
|
||||
},
|
||||
render() {
|
||||
return toggle.value ? h('div', { ref: 'refKey' }) : null
|
||||
return toggle.value ? h('div', { ref: [this, 'refKey'] as any }) : null
|
||||
}
|
||||
}
|
||||
render(h(Comp), root)
|
||||
|
@ -33,6 +33,7 @@ export type ComponentPublicInstance<
|
||||
M extends MethodOptions = {},
|
||||
PublicProps = P
|
||||
> = {
|
||||
$: ComponentInternalInstance
|
||||
$data: D
|
||||
$props: PublicProps
|
||||
$attrs: Data
|
||||
|
@ -30,7 +30,8 @@ import {
|
||||
isFunction,
|
||||
PatchFlags,
|
||||
ShapeFlags,
|
||||
NOOP
|
||||
NOOP,
|
||||
isArray
|
||||
} from '@vue/shared'
|
||||
import {
|
||||
queueJob,
|
||||
@ -1793,11 +1794,19 @@ function baseCreateRenderer<
|
||||
}
|
||||
|
||||
const setRef = (
|
||||
ref: string | Function | Ref,
|
||||
oldRef: string | Function | Ref | null,
|
||||
ref: string | Function | Ref | [ComponentPublicInstance, string],
|
||||
oldRef: string | Function | Ref | [ComponentPublicInstance, string] | null,
|
||||
parent: ComponentInternalInstance,
|
||||
value: HostNode | ComponentPublicInstance | null
|
||||
) => {
|
||||
if (isArray(ref)) {
|
||||
// template string refs are compiled into tuples like [ctx, key] to
|
||||
// ensure refs inside slots are set on the correct owner instance.
|
||||
const [{ $: owner }, key] = ref
|
||||
setRef(key, oldRef && (oldRef as any[])[1], owner, value)
|
||||
return
|
||||
}
|
||||
|
||||
const refs = parent.refs === EMPTY_OBJ ? (parent.refs = {}) : parent.refs
|
||||
const renderContext = toRaw(parent.renderContext)
|
||||
|
||||
@ -1823,7 +1832,7 @@ function baseCreateRenderer<
|
||||
} else if (isRef(ref)) {
|
||||
ref.value = value
|
||||
} else if (isFunction(ref)) {
|
||||
callWithErrorHandling(ref, parent, ErrorCodes.FUNCTION_REF, [value, refs])
|
||||
callWithErrorHandling(ref, parent, ErrorCodes.FUNCTION_REF, [value])
|
||||
} else if (__DEV__) {
|
||||
warn('Invalid template ref type:', value, `(${typeof value})`)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user