فهرست منبع

fix(compiler-core): force <svg> into blocks for correct runtime isSVG
state during patch

Evan You 6 سال پیش
والد
کامیت
f2ac28b31e

+ 24 - 1
packages/compiler-core/__tests__/transforms/transformElement.spec.ts

@@ -16,7 +16,9 @@ import {
   RESOLVE_DYNAMIC_COMPONENT,
   SUSPENSE,
   KEEP_ALIVE,
-  BASE_TRANSITION
+  BASE_TRANSITION,
+  OPEN_BLOCK,
+  CREATE_BLOCK
 } from '../../src/runtimeHelpers'
 import {
   CallExpression,
@@ -821,4 +823,25 @@ describe('compiler: element transform', () => {
       ])
     })
   })
+
+  test('<svg> should be forced into blocks', () => {
+    const ast = parse(`<div><svg/></div>`)
+    transform(ast, {
+      nodeTransforms: [transformElement]
+    })
+    expect((ast as any).children[0].children[0].codegenNode).toMatchObject({
+      type: NodeTypes.JS_SEQUENCE_EXPRESSION,
+      expressions: [
+        {
+          type: NodeTypes.JS_CALL_EXPRESSION,
+          callee: OPEN_BLOCK
+        },
+        {
+          type: NodeTypes.JS_CALL_EXPRESSION,
+          callee: CREATE_BLOCK,
+          arguments: [`"svg"`]
+        }
+      ]
+    })
+  })
 })

+ 7 - 2
packages/compiler-core/src/ast.ts

@@ -118,6 +118,7 @@ export interface BaseElementNode extends Node {
     | CallExpression
     | SimpleExpressionNode
     | CacheExpression
+    | SequenceExpression
     | undefined
 }
 
@@ -125,14 +126,18 @@ export interface PlainElementNode extends BaseElementNode {
   tagType: ElementTypes.ELEMENT
   codegenNode:
     | ElementCodegenNode
-    | undefined
     | SimpleExpressionNode // when hoisted
     | CacheExpression // when cached by v-once
+    | SequenceExpression // when turned into a block
+    | undefined
 }
 
 export interface ComponentNode extends BaseElementNode {
   tagType: ElementTypes.COMPONENT
-  codegenNode: ComponentCodegenNode | undefined | CacheExpression // when cached by v-once
+  codegenNode:
+    | ComponentCodegenNode
+    | CacheExpression // when cached by v-once
+    | undefined
 }
 
 export interface SlotOutletNode extends BaseElementNode {

+ 17 - 4
packages/compiler-core/src/transforms/transformElement.ts

@@ -13,7 +13,8 @@ import {
   createObjectProperty,
   createSimpleExpression,
   createObjectExpression,
-  Property
+  Property,
+  createSequenceExpression
 } from '../ast'
 import { PatchFlags, PatchFlagNames, isSymbol } from '@vue/shared'
 import { createCompilerError, ErrorCodes } from '../errors'
@@ -26,7 +27,9 @@ import {
   MERGE_PROPS,
   TO_HANDLERS,
   PORTAL,
-  KEEP_ALIVE
+  KEEP_ALIVE,
+  OPEN_BLOCK,
+  CREATE_BLOCK
 } from '../runtimeHelpers'
 import {
   getInnerRange,
@@ -67,6 +70,9 @@ export const transformElement: NodeTransform = (node, context) => {
     let runtimeDirectives: DirectiveNode[] | undefined
     let dynamicPropNames: string[] | undefined
     let dynamicComponent: string | CallExpression | undefined
+    // technically this is web specific but we are keeping it in core to avoid
+    // extra complexity
+    let isSVG = false
 
     // handle dynamic component
     const isProp = findProp(node, 'is')
@@ -105,6 +111,7 @@ export const transformElement: NodeTransform = (node, context) => {
     } else {
       // plain element
       nodeType = `"${node.tag}"`
+      isSVG = node.tag === 'svg'
     }
 
     const args: CallExpression['arguments'] = [nodeType]
@@ -190,8 +197,14 @@ export const transformElement: NodeTransform = (node, context) => {
     }
 
     const { loc } = node
-    const vnode = createCallExpression(context.helper(CREATE_VNODE), args, loc)
-
+    const vnode = isSVG
+      ? // <svg> must be forced into blocks so that block updates inside retain
+        // isSVG flag at runtime. (#639, #643)
+        createSequenceExpression([
+          createCallExpression(context.helper(OPEN_BLOCK)),
+          createCallExpression(context.helper(CREATE_BLOCK), args, loc)
+        ])
+      : createCallExpression(context.helper(CREATE_VNODE), args, loc)
     if (runtimeDirectives && runtimeDirectives.length) {
       node.codegenNode = createCallExpression(
         context.helper(WITH_DIRECTIVES),