fix(compiler/v-on): handle multiple statements in v-on handler (close #572)
This commit is contained in:
		
							parent
							
								
									46a793717a
								
							
						
					
					
						commit
						137893a4fd
					
				@ -140,6 +140,22 @@ describe('compiler: transform v-on', () => {
 | 
				
			|||||||
    })
 | 
					    })
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('should handle multiple inline statement', () => {
 | 
				
			||||||
 | 
					    const { node } = parseWithVOn(`<div @click="foo();bar()"/>`)
 | 
				
			||||||
 | 
					    const props = (node.codegenNode as CallExpression)
 | 
				
			||||||
 | 
					      .arguments[1] as ObjectExpression
 | 
				
			||||||
 | 
					    expect(props.properties[0]).toMatchObject({
 | 
				
			||||||
 | 
					      key: { content: `onClick` },
 | 
				
			||||||
 | 
					      value: {
 | 
				
			||||||
 | 
					        type: NodeTypes.COMPOUND_EXPRESSION,
 | 
				
			||||||
 | 
					        // should wrap with `{` for multiple statements
 | 
				
			||||||
 | 
					        // in this case the return value is discarded and the behavior is
 | 
				
			||||||
 | 
					        // consistent with 2.x
 | 
				
			||||||
 | 
					        children: [`$event => {`, { content: `foo();bar()` }, `}`]
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  test('inline statement w/ prefixIdentifiers: true', () => {
 | 
					  test('inline statement w/ prefixIdentifiers: true', () => {
 | 
				
			||||||
    const { node } = parseWithVOn(`<div @click="foo($event)"/>`, {
 | 
					    const { node } = parseWithVOn(`<div @click="foo($event)"/>`, {
 | 
				
			||||||
      prefixIdentifiers: true
 | 
					      prefixIdentifiers: true
 | 
				
			||||||
@ -163,6 +179,31 @@ describe('compiler: transform v-on', () => {
 | 
				
			|||||||
    })
 | 
					    })
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('multiple inline statements w/ prefixIdentifiers: true', () => {
 | 
				
			||||||
 | 
					    const { node } = parseWithVOn(`<div @click="foo($event);bar()"/>`, {
 | 
				
			||||||
 | 
					      prefixIdentifiers: true
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    const props = (node.codegenNode as CallExpression)
 | 
				
			||||||
 | 
					      .arguments[1] as ObjectExpression
 | 
				
			||||||
 | 
					    expect(props.properties[0]).toMatchObject({
 | 
				
			||||||
 | 
					      key: { content: `onClick` },
 | 
				
			||||||
 | 
					      value: {
 | 
				
			||||||
 | 
					        type: NodeTypes.COMPOUND_EXPRESSION,
 | 
				
			||||||
 | 
					        children: [
 | 
				
			||||||
 | 
					          `$event => {`,
 | 
				
			||||||
 | 
					          { content: `_ctx.foo` },
 | 
				
			||||||
 | 
					          `(`,
 | 
				
			||||||
 | 
					          // should NOT prefix $event
 | 
				
			||||||
 | 
					          { content: `$event` },
 | 
				
			||||||
 | 
					          `);`,
 | 
				
			||||||
 | 
					          { content: `_ctx.bar` },
 | 
				
			||||||
 | 
					          `()`,
 | 
				
			||||||
 | 
					          `}`
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  test('should NOT wrap as function if expression is already function expression', () => {
 | 
					  test('should NOT wrap as function if expression is already function expression', () => {
 | 
				
			||||||
    const { node } = parseWithVOn(`<div @click="$event => foo($event)"/>`)
 | 
					    const { node } = parseWithVOn(`<div @click="$event => foo($event)"/>`)
 | 
				
			||||||
    const props = (node.codegenNode as CallExpression)
 | 
					    const props = (node.codegenNode as CallExpression)
 | 
				
			||||||
 | 
				
			|||||||
@ -76,7 +76,9 @@ export function processExpression(
 | 
				
			|||||||
  context: TransformContext,
 | 
					  context: TransformContext,
 | 
				
			||||||
  // some expressions like v-slot props & v-for aliases should be parsed as
 | 
					  // some expressions like v-slot props & v-for aliases should be parsed as
 | 
				
			||||||
  // function params
 | 
					  // function params
 | 
				
			||||||
  asParams: boolean = false
 | 
					  asParams = false,
 | 
				
			||||||
 | 
					  // v-on handler values may contain multiple statements
 | 
				
			||||||
 | 
					  asRawStatements = false
 | 
				
			||||||
): ExpressionNode {
 | 
					): ExpressionNode {
 | 
				
			||||||
  if (!context.prefixIdentifiers || !node.content.trim()) {
 | 
					  if (!context.prefixIdentifiers || !node.content.trim()) {
 | 
				
			||||||
    return node
 | 
					    return node
 | 
				
			||||||
@ -100,9 +102,14 @@ export function processExpression(
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let ast: any
 | 
					  let ast: any
 | 
				
			||||||
  // if the expression is supposed to be used in a function params position
 | 
					  // exp needs to be parsed differently:
 | 
				
			||||||
  // we need to parse it differently.
 | 
					  // 1. Multiple inline statements (v-on, with presence of `;`): parse as raw
 | 
				
			||||||
  const source = `(${rawExp})${asParams ? `=>{}` : ``}`
 | 
					  //    exp, but make sure to pad with spaces for consistent ranges
 | 
				
			||||||
 | 
					  // 2. Expressions: wrap with parens (for e.g. object expressions)
 | 
				
			||||||
 | 
					  // 3. Function arguments (v-for, v-slot): place in a function argument position
 | 
				
			||||||
 | 
					  const source = asRawStatements
 | 
				
			||||||
 | 
					    ? ` ${rawExp} `
 | 
				
			||||||
 | 
					    : `(${rawExp})${asParams ? `=>{}` : ``}`
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    ast = parseJS(source, { ranges: true })
 | 
					    ast = parseJS(source, { ranges: true })
 | 
				
			||||||
  } catch (e) {
 | 
					  } catch (e) {
 | 
				
			||||||
 | 
				
			|||||||
@ -59,11 +59,12 @@ export const transformOn: DirectiveTransform = (
 | 
				
			|||||||
  if (exp) {
 | 
					  if (exp) {
 | 
				
			||||||
    const isMemberExp = isMemberExpression(exp.content)
 | 
					    const isMemberExp = isMemberExpression(exp.content)
 | 
				
			||||||
    const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content))
 | 
					    const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content))
 | 
				
			||||||
 | 
					    const hasMultipleStatements = exp.content.includes(`;`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // process the expression since it's been skipped
 | 
					    // process the expression since it's been skipped
 | 
				
			||||||
    if (!__BROWSER__ && context.prefixIdentifiers) {
 | 
					    if (!__BROWSER__ && context.prefixIdentifiers) {
 | 
				
			||||||
      context.addIdentifiers(`$event`)
 | 
					      context.addIdentifiers(`$event`)
 | 
				
			||||||
      exp = processExpression(exp, context)
 | 
					      exp = processExpression(exp, context, false, hasMultipleStatements)
 | 
				
			||||||
      context.removeIdentifiers(`$event`)
 | 
					      context.removeIdentifiers(`$event`)
 | 
				
			||||||
      // with scope analysis, the function is hoistable if it has no reference
 | 
					      // with scope analysis, the function is hoistable if it has no reference
 | 
				
			||||||
      // to scope variables.
 | 
					      // to scope variables.
 | 
				
			||||||
@ -85,9 +86,9 @@ export const transformOn: DirectiveTransform = (
 | 
				
			|||||||
    if (isInlineStatement || (isCacheable && isMemberExp)) {
 | 
					    if (isInlineStatement || (isCacheable && isMemberExp)) {
 | 
				
			||||||
      // wrap inline statement in a function expression
 | 
					      // wrap inline statement in a function expression
 | 
				
			||||||
      exp = createCompoundExpression([
 | 
					      exp = createCompoundExpression([
 | 
				
			||||||
        `$event => (`,
 | 
					        `$event => ${hasMultipleStatements ? `{` : `(`}`,
 | 
				
			||||||
        ...(exp.type === NodeTypes.SIMPLE_EXPRESSION ? [exp] : exp.children),
 | 
					        ...(exp.type === NodeTypes.SIMPLE_EXPRESSION ? [exp] : exp.children),
 | 
				
			||||||
        `)`
 | 
					        hasMultipleStatements ? `}` : `)`
 | 
				
			||||||
      ])
 | 
					      ])
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user