Просмотр исходного кода

fix(compiler-vapor): treat attribute as dynamic if has dynamic key prop

三咲智子 Kevin Deng 2 лет назад
Родитель
Сommit
2229d3ce20

+ 14 - 0
packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap

@@ -169,6 +169,20 @@ export function render(_ctx) {
 }"
 `;
 
+exports[`compiler v-bind > dynamic arg w/ static attribute 1`] = `
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProps as _setDynamicProps } from 'vue/vapor';
+
+export function render(_ctx) {
+  const t0 = _template("<div></div>")
+  const n0 = t0()
+  const { 0: [n1],} = _children(n0)
+  _renderEffect(() => {
+    _setDynamicProps(n1, { [_ctx.id]: _ctx.id, foo: "bar", checked: "" })
+  })
+  return n0
+}"
+`;
+
 exports[`compiler v-bind > no expression (shorthand) 1`] = `
 "import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
 

+ 49 - 0
packages/compiler-vapor/__tests__/transforms/vBind.spec.ts

@@ -167,6 +167,55 @@ describe('compiler v-bind', () => {
     )
   })
 
+  test('dynamic arg w/ static attribute', () => {
+    const { ir, code } = compileWithVBind(
+      `<div v-bind:[id]="id" foo="bar" checked />`,
+    )
+    expect(code).matchSnapshot()
+    expect(ir.effect[0].operations[0]).toMatchObject({
+      type: IRNodeTypes.SET_DYNAMIC_PROPS,
+      element: 1,
+      props: [
+        [
+          {
+            key: {
+              type: NodeTypes.SIMPLE_EXPRESSION,
+              content: 'id',
+              isStatic: false,
+            },
+            value: {
+              type: NodeTypes.SIMPLE_EXPRESSION,
+              content: 'id',
+              isStatic: false,
+            },
+          },
+          {
+            key: {
+              type: NodeTypes.SIMPLE_EXPRESSION,
+              content: 'foo',
+              isStatic: true,
+            },
+            value: {
+              type: NodeTypes.SIMPLE_EXPRESSION,
+              content: 'bar',
+              isStatic: true,
+            },
+          },
+          {
+            key: {
+              type: NodeTypes.SIMPLE_EXPRESSION,
+              content: 'checked',
+              isStatic: true,
+            },
+          },
+        ],
+      ],
+    })
+    expect(code).contains(
+      '_setDynamicProps(n1, { [_ctx.id]: _ctx.id, foo: "bar", checked: "" })',
+    )
+  })
+
   test('should error if empty expression', () => {
     const onError = vi.fn()
     const { ir, code } = compileWithVBind(`<div v-bind:arg="" />`, {

+ 0 - 2
packages/compiler-vapor/src/transform.ts

@@ -10,7 +10,6 @@ import {
   type ParentNode,
   type RootNode,
   type SimpleExpressionNode,
-  type SourceLocation,
   type TemplateChildNode,
   type TemplateNode,
   defaultOnError,
@@ -46,7 +45,6 @@ export type DirectiveTransform = (
 export interface DirectiveTransformResult {
   key: SimpleExpressionNode
   value: SimpleExpressionNode
-  loc: SourceLocation
   modifier?: '.' | '^'
   runtimeCamelize?: boolean
 }

+ 33 - 10
packages/compiler-vapor/src/transforms/transformElement.ts

@@ -6,6 +6,7 @@ import {
   NodeTypes,
   type SimpleExpressionNode,
   createCompilerError,
+  createSimpleExpression,
 } from '@vue/compiler-dom'
 import { isBuiltInDirective, isReservedProp, isVoidTag } from '@vue/shared'
 import type {
@@ -57,16 +58,18 @@ export const transformElement: NodeTransform = (node, context) => {
 function buildProps(
   node: ElementNode,
   context: TransformContext<ElementNode>,
-  props: ElementNode['props'] = node.props,
+  props: (VaporDirectiveNode | AttributeNode)[] = node.props as any,
   isComponent: boolean,
 ) {
   const dynamicArgs: PropsExpression[] = []
   const dynamicExpr: SimpleExpressionNode[] = []
   let results: DirectiveTransformResult[] = []
 
-  function pushExpressions(...exprs: SimpleExpressionNode[]) {
+  function pushDynamicExpressions(
+    ...exprs: (SimpleExpressionNode | undefined)[]
+  ) {
     for (const expr of exprs) {
-      if (!expr.isStatic) dynamicExpr.push(expr)
+      if (expr && !expr.isStatic) dynamicExpr.push(expr)
     }
   }
 
@@ -77,14 +80,22 @@ function buildProps(
     }
   }
 
-  for (const prop of props as (VaporDirectiveNode | AttributeNode)[]) {
+  // treat all props as dynamic key
+  const asDynamic = props.some(
+    prop =>
+      prop.type === NodeTypes.DIRECTIVE &&
+      prop.name === 'bind' &&
+      (!prop.arg || !prop.arg.isStatic),
+  )
+
+  for (const prop of props) {
     if (
       prop.type === NodeTypes.DIRECTIVE &&
       prop.name === 'bind' &&
       !prop.arg
     ) {
       if (prop.exp) {
-        pushExpressions(prop.exp)
+        pushDynamicExpressions(prop.exp)
         pushMergeArg()
         dynamicArgs.push(prop.exp)
       } else {
@@ -95,10 +106,10 @@ function buildProps(
       continue
     }
 
-    const result = transformProp(prop, node, context)
+    const result = transformProp(prop, node, context, asDynamic)
     if (result) {
       results.push(result)
-      pushExpressions(result.key, result.value)
+      asDynamic && pushDynamicExpressions(result.key, result.value)
     }
   }
 
@@ -136,14 +147,26 @@ function transformProp(
   prop: VaporDirectiveNode | AttributeNode,
   node: ElementNode,
   context: TransformContext<ElementNode>,
+  asDynamic: boolean,
 ): DirectiveTransformResult | void {
   const { name } = prop
   if (isReservedProp(name)) return
 
   if (prop.type === NodeTypes.ATTRIBUTE) {
-    context.template += ` ${name}`
-    if (prop.value) context.template += `="${prop.value.content}"`
-    return
+    if (asDynamic) {
+      return {
+        key: createSimpleExpression(prop.name, true, prop.nameLoc),
+        value: createSimpleExpression(
+          prop.value ? prop.value.content : '',
+          true,
+          prop.value && prop.value.loc,
+        ),
+      }
+    } else {
+      context.template += ` ${name}`
+      if (prop.value) context.template += `="${prop.value.content}"`
+      return
+    }
   }
 
   const directiveTransform = context.options.directiveTransforms[name]