mirror of https://github.com/vuejs/core.git
feat(compiler-vapor): complex identifier rewriting with vOn (#113)
Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
This commit is contained in:
parent
e9e7fe414b
commit
75b0937d31
|
@ -353,6 +353,18 @@ export function render(_ctx) {
|
|||
}"
|
||||
`;
|
||||
|
||||
exports[`v-on > should wrap in unref if identifier is setup-maybe-ref w/ inline: true 1`] = `
|
||||
"(() => {
|
||||
const t0 = _template("<div></div><div></div><div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1], 1: [n2], 2: [n3],} = _children(n0)
|
||||
_on(n1, "click", $event => (x.value=_unref(y)))
|
||||
_on(n2, "click", $event => (x.value++))
|
||||
_on(n3, "click", $event => ({ x: x.value } = _unref(y)))
|
||||
return n0
|
||||
})()"
|
||||
`;
|
||||
|
||||
exports[`v-on > should wrap keys guard for keyboard events or dynamic events 1`] = `
|
||||
"import { template as _template, children as _children, withModifiers as _withModifiers, withKeys as _withKeys, on as _on } from 'vue/vapor';
|
||||
|
||||
|
|
|
@ -170,6 +170,29 @@ describe('v-on', () => {
|
|||
expect(code).contains('_on(n1, "click", $event => (_ctx.i++))')
|
||||
})
|
||||
|
||||
test('should wrap in unref if identifier is setup-maybe-ref w/ inline: true', () => {
|
||||
const { code, helpers, vaporHelpers } = compileWithVOn(
|
||||
`<div @click="x=y"/><div @click="x++"/><div @click="{ x } = y"/>`,
|
||||
{
|
||||
mode: 'module',
|
||||
inline: true,
|
||||
bindingMetadata: {
|
||||
x: BindingTypes.SETUP_MAYBE_REF,
|
||||
y: BindingTypes.SETUP_MAYBE_REF,
|
||||
},
|
||||
},
|
||||
)
|
||||
expect(code).matchSnapshot()
|
||||
|
||||
expect(vaporHelpers).contains('unref')
|
||||
expect(helpers.size).toBe(0)
|
||||
expect(code).contains('_on(n1, "click", $event => (x.value=_unref(y)))')
|
||||
expect(code).contains('_on(n2, "click", $event => (x.value++))')
|
||||
expect(code).contains(
|
||||
'_on(n3, "click", $event => ({ x: x.value } = _unref(y)))',
|
||||
)
|
||||
})
|
||||
|
||||
test('should handle multiple inline statement', () => {
|
||||
const { ir, code } = compileWithVOn(`<div @click="foo();bar()"/>`)
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ import {
|
|||
NewlineType,
|
||||
type SourceLocation,
|
||||
advancePositionWithClone,
|
||||
isInDestructureAssignment,
|
||||
isStaticProperty,
|
||||
walkIdentifiers,
|
||||
} from '@vue/compiler-dom'
|
||||
import { isGloballyAllowed, isString, makeMap } from '@vue/shared'
|
||||
|
@ -13,6 +15,7 @@ import {
|
|||
type CodegenContext,
|
||||
buildCodeFragment,
|
||||
} from '../generate'
|
||||
import type { Node } from '@babel/types'
|
||||
|
||||
export function genExpression(
|
||||
node: IRExpression,
|
||||
|
@ -42,11 +45,21 @@ export function genExpression(
|
|||
|
||||
// the expression is a simple identifier
|
||||
if (ast === null) {
|
||||
return [genIdentifier(rawExpr, context, loc)]
|
||||
return genIdentifier(rawExpr, context, loc)
|
||||
}
|
||||
|
||||
const ids: Identifier[] = []
|
||||
walkIdentifiers(ast!, id => ids.push(id))
|
||||
const parentStackMap = new WeakMap<Identifier, Node[]>()
|
||||
const parentStack: Node[] = []
|
||||
walkIdentifiers(
|
||||
ast!,
|
||||
id => {
|
||||
ids.push(id)
|
||||
parentStackMap.set(id, parentStack.slice())
|
||||
},
|
||||
false,
|
||||
parentStack,
|
||||
)
|
||||
if (ids.length) {
|
||||
ids.sort((a, b) => a.start! - b.start!)
|
||||
const [frag, push] = buildCodeFragment()
|
||||
|
@ -60,12 +73,20 @@ export function genExpression(
|
|||
if (leadingText.length) push([leadingText, NewlineType.Unknown])
|
||||
|
||||
const source = rawExpr.slice(start, end)
|
||||
const parentStack = parentStackMap.get(id)!
|
||||
push(
|
||||
genIdentifier(source, context, {
|
||||
start: advancePositionWithClone(node.loc.start, source, start),
|
||||
end: advancePositionWithClone(node.loc.start, source, end),
|
||||
...genIdentifier(
|
||||
source,
|
||||
}),
|
||||
context,
|
||||
{
|
||||
start: advancePositionWithClone(node.loc.start, source, start),
|
||||
end: advancePositionWithClone(node.loc.start, source, end),
|
||||
source,
|
||||
},
|
||||
id,
|
||||
parentStack[parentStack.length - 1],
|
||||
parentStack,
|
||||
),
|
||||
)
|
||||
|
||||
if (i === ids.length - 1 && end < rawExpr.length) {
|
||||
|
@ -81,30 +102,55 @@ export function genExpression(
|
|||
const isLiteralWhitelisted = /*#__PURE__*/ makeMap('true,false,null,this')
|
||||
|
||||
function genIdentifier(
|
||||
id: string,
|
||||
raw: string,
|
||||
{ options, vaporHelper, identifiers }: CodegenContext,
|
||||
loc?: SourceLocation,
|
||||
): CodeFragment {
|
||||
id?: Identifier,
|
||||
parent?: Node,
|
||||
parentStack?: Node[],
|
||||
): CodeFragment[] {
|
||||
const { inline, bindingMetadata } = options
|
||||
let name: string | undefined = id
|
||||
let name: string | undefined = raw
|
||||
|
||||
const idMap = identifiers[id]
|
||||
const idMap = identifiers[raw]
|
||||
if (idMap && idMap.length) {
|
||||
return [idMap[0], NewlineType.None, loc]
|
||||
return [[idMap[0], NewlineType.None, loc]]
|
||||
}
|
||||
|
||||
let prefix: string | undefined
|
||||
if (isStaticProperty(parent!) && parent.shorthand) {
|
||||
// property shorthand like { foo }, we need to add the key since
|
||||
// we rewrite the value
|
||||
prefix = `${raw}: `
|
||||
}
|
||||
|
||||
if (inline) {
|
||||
switch (bindingMetadata[id]) {
|
||||
switch (bindingMetadata[raw]) {
|
||||
case BindingTypes.SETUP_REF:
|
||||
name = id += '.value'
|
||||
name = raw = `${raw}.value`
|
||||
break
|
||||
case BindingTypes.SETUP_MAYBE_REF:
|
||||
id = `${vaporHelper('unref')}(${id})`
|
||||
name = undefined
|
||||
// ({ x } = y)
|
||||
const isDestructureAssignment =
|
||||
parent && isInDestructureAssignment(parent, parentStack || [])
|
||||
// x = y
|
||||
const isAssignmentLVal =
|
||||
parent && parent.type === 'AssignmentExpression' && parent.left === id
|
||||
// x++
|
||||
const isUpdateArg =
|
||||
parent && parent.type === 'UpdateExpression' && parent.argument === id
|
||||
// const binding that may or may not be ref
|
||||
// if it's not a ref, then assignments don't make sense -
|
||||
// so we ignore the non-ref assignment case and generate code
|
||||
// that assumes the value to be a ref for more efficiency
|
||||
raw =
|
||||
isAssignmentLVal || isUpdateArg || isDestructureAssignment
|
||||
? (name = `${raw}.value`)
|
||||
: `${vaporHelper('unref')}(${raw})`
|
||||
break
|
||||
}
|
||||
} else {
|
||||
id = `_ctx.${id}`
|
||||
raw = `_ctx.${raw}`
|
||||
}
|
||||
return [id, NewlineType.None, loc, name]
|
||||
return [prefix, [raw, NewlineType.None, loc, name]]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue