]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(compiler): generate patchFlags for runtime
authorEvan You <yyx990803@gmail.com>
Tue, 1 Oct 2019 01:17:12 +0000 (21:17 -0400)
committerEvan You <yyx990803@gmail.com>
Tue, 1 Oct 2019 01:17:12 +0000 (21:17 -0400)
19 files changed:
packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap
packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
packages/compiler-core/__tests__/transforms/transformElement.spec.ts
packages/compiler-core/__tests__/transforms/vSlot.spec.ts
packages/compiler-core/src/ast.ts
packages/compiler-core/src/codegen.ts
packages/compiler-core/src/transform.ts
packages/compiler-core/src/transforms/optimizeBlocks.ts [deleted file]
packages/compiler-core/src/transforms/optimizePatchFlags.ts [deleted file]
packages/compiler-core/src/transforms/transformElement.ts
packages/compiler-core/src/transforms/vSlot.ts
packages/runtime-core/__tests__/rendererFragment.spec.ts
packages/runtime-core/src/componentProps.ts
packages/runtime-core/src/componentRenderUtils.ts
packages/runtime-core/src/createRenderer.ts
packages/runtime-core/src/index.ts
packages/runtime-core/src/vnode.ts
packages/shared/src/index.ts
packages/shared/src/patchFlags.ts [moved from packages/runtime-core/src/patchFlags.ts with 90% similarity]

index f73f3dd7a10a8a447a00fa3c1a85a596ec29de24..047d00ad3de26d8b93d5be147398cb977a307507 100644 (file)
@@ -11,14 +11,14 @@ return function render() {
     }, [
       _toString(world.burn()),
       ok
-        ? _createVNode(\\"div\\", 0, \\"yes\\")
+        ? _createVNode(\\"div\\", null, \\"yes\\")
         : \\"no\\",
       _renderList(list, (value, index) => {
-        return _createVNode(\\"div\\", 0, [
-          _createVNode(\\"span\\", 0, _toString(value + index))
+        return _createVNode(\\"div\\", null, [
+          _createVNode(\\"span\\", null, _toString(value + index))
         ])
       })
-    ])
+    ], 2)
   }
 }"
 `;
@@ -34,14 +34,14 @@ return function render() {
   }, [
     toString(_ctx.world.burn()),
     (_ctx.ok)
-      ? createVNode(\\"div\\", 0, \\"yes\\")
+      ? createVNode(\\"div\\", null, \\"yes\\")
       : \\"no\\",
     renderList(_ctx.list, (value, index) => {
-      return createVNode(\\"div\\", 0, [
-        createVNode(\\"span\\", 0, toString(value + index))
+      return createVNode(\\"div\\", null, [
+        createVNode(\\"span\\", null, toString(value + index))
       ])
     })
-  ])
+  ], 2)
 }"
 `;
 
@@ -56,13 +56,13 @@ export default function render() {
   }, [
     _toString(_ctx.world.burn()),
     (_ctx.ok)
-      ? createVNode(\\"div\\", 0, \\"yes\\")
+      ? createVNode(\\"div\\", null, \\"yes\\")
       : \\"no\\",
     _renderList(_ctx.list, (value, index) => {
-      return createVNode(\\"div\\", 0, [
-        createVNode(\\"span\\", 0, _toString(value + index))
+      return createVNode(\\"div\\", null, [
+        createVNode(\\"span\\", null, _toString(value + index))
       ])
     })
-  ])
+  ], 2)
 }"
 `;
index 1ef19853b4985c0b00ce9f0edc8684bc7be74fb1..1bc471f328c5eec634f38576e0d379d3ce307fa1 100644 (file)
@@ -7,7 +7,7 @@ return function render() {
   const _ctx = this
   const _component_Comp = resolveComponent(\\"Comp\\")
   
-  return createVNode(_component_Comp, 0, {
+  return createVNode(_component_Comp, null, {
     [_ctx.one]: ({ foo }) => [
       toString(foo),
       toString(_ctx.bar)
@@ -16,7 +16,7 @@ return function render() {
       toString(_ctx.foo),
       toString(bar)
     ]
-  })
+  }, 256)
 }"
 `;
 
@@ -27,7 +27,7 @@ return function render() {
   const _ctx = this
   const _component_Comp = resolveComponent(\\"Comp\\")
   
-  return createVNode(_component_Comp, 0, {
+  return createVNode(_component_Comp, null, {
     default: ({ foo }) => [
       toString(foo),
       toString(_ctx.bar)
@@ -43,7 +43,7 @@ return function render() {
   const _ctx = this
   const _component_Comp = resolveComponent(\\"Comp\\")
   
-  return createVNode(_component_Comp, 0, {
+  return createVNode(_component_Comp, null, {
     default: () => [
       createVNode(\\"div\\")
     ]
@@ -58,7 +58,7 @@ return function render() {
   const _ctx = this
   const _component_Comp = resolveComponent(\\"Comp\\")
   
-  return createVNode(_component_Comp, 0, {
+  return createVNode(_component_Comp, null, {
     one: ({ foo }) => [
       toString(foo),
       toString(_ctx.bar)
@@ -79,9 +79,9 @@ return function render() {
   const _component_Comp = resolveComponent(\\"Comp\\")
   const _component_Inner = resolveComponent(\\"Inner\\")
   
-  return createVNode(_component_Comp, 0, {
+  return createVNode(_component_Comp, null, {
     default: ({ foo }) => [
-      createVNode(_component_Inner, 0, {
+      createVNode(_component_Inner, null, {
         default: ({ bar }) => [
           toString(foo),
           toString(bar),
index f35092e5150425954f7b5d9c00aab3a67203de4d..2af63cb8b6c9265244079ef186a5cde7e619805d 100644 (file)
@@ -24,6 +24,7 @@ import { transformElement } from '../../src/transforms/transformElement'
 import { transformOn } from '../../src/transforms/vOn'
 import { transformStyle } from '../../src/transforms/transformStyle'
 import { transformBind } from '../../src/transforms/vBind'
+import { PatchFlags } from '@vue/shared'
 
 function parseWithElementTransform(
   template: string,
@@ -127,7 +128,7 @@ describe('compiler: element transform', () => {
     expect(node.callee).toBe(`_${CREATE_VNODE}`)
     expect(node.arguments).toMatchObject([
       `"div"`,
-      `0`,
+      `null`,
       [
         {
           type: NodeTypes.ELEMENT,
@@ -351,7 +352,9 @@ describe('compiler: element transform', () => {
                 value: _dir!.exp
               }
             ]
-          }
+          },
+          `null`,
+          String(PatchFlags.NEED_PATCH) // should generate appropriate flag
         ]
       },
       {
@@ -546,5 +549,121 @@ describe('compiler: element transform', () => {
     })
   })
 
-  test.todo('slot outlets')
+  test(`props merging: class`, () => {
+    const { node } = parseWithElementTransform(
+      `<div class="foo" :class="{ bar: isBar }" />`,
+      {
+        directiveTransforms: {
+          bind: transformBind
+        }
+      }
+    )
+    expect(node.arguments[1]).toMatchObject({
+      type: NodeTypes.JS_OBJECT_EXPRESSION,
+      properties: [
+        {
+          type: NodeTypes.JS_PROPERTY,
+          key: {
+            type: NodeTypes.SIMPLE_EXPRESSION,
+            content: `class`,
+            isStatic: true
+          },
+          value: {
+            type: NodeTypes.JS_ARRAY_EXPRESSION,
+            elements: [
+              {
+                type: NodeTypes.SIMPLE_EXPRESSION,
+                content: `foo`,
+                isStatic: true
+              },
+              {
+                type: NodeTypes.SIMPLE_EXPRESSION,
+                content: `{ bar: isBar }`,
+                isStatic: false
+              }
+            ]
+          }
+        }
+      ]
+    })
+  })
+
+  describe('patchFlag analysis', () => {
+    function parseWithBind(template: string) {
+      return parseWithElementTransform(template, {
+        directiveTransforms: {
+          bind: transformBind
+        }
+      })
+    }
+
+    test('CLASS', () => {
+      const { node } = parseWithBind(`<div :class="foo" />`)
+      expect(node.arguments.length).toBe(4)
+      expect(node.arguments[3]).toBe(String(PatchFlags.CLASS))
+    })
+
+    test('STYLE', () => {
+      const { node } = parseWithBind(`<div :style="foo" />`)
+      expect(node.arguments.length).toBe(4)
+      expect(node.arguments[3]).toBe(String(PatchFlags.STYLE))
+    })
+
+    test('PROPS', () => {
+      const { node } = parseWithBind(`<div id="foo" :foo="bar" :baz="qux" />`)
+      expect(node.arguments.length).toBe(5)
+      expect(node.arguments[3]).toBe(String(PatchFlags.PROPS))
+      expect(node.arguments[4]).toBe(`["foo", "baz"]`)
+    })
+
+    test('CLASS + STYLE + PROPS', () => {
+      const { node } = parseWithBind(
+        `<div id="foo" :class="cls" :style="styl" :foo="bar" :baz="qux"/>`
+      )
+      expect(node.arguments.length).toBe(5)
+      expect(node.arguments[3]).toBe(
+        String(PatchFlags.PROPS | PatchFlags.CLASS | PatchFlags.STYLE)
+      )
+      expect(node.arguments[4]).toBe(`["foo", "baz"]`)
+    })
+
+    test('FULL_PROPS (v-bind)', () => {
+      const { node } = parseWithBind(`<div v-bind="foo" />`)
+      expect(node.arguments.length).toBe(4)
+      expect(node.arguments[3]).toBe(String(PatchFlags.FULL_PROPS))
+    })
+
+    test('FULL_PROPS (dynamic key)', () => {
+      const { node } = parseWithBind(`<div :[foo]="bar" />`)
+      expect(node.arguments.length).toBe(4)
+      expect(node.arguments[3]).toBe(String(PatchFlags.FULL_PROPS))
+    })
+
+    test('FULL_PROPS (w/ others)', () => {
+      const { node } = parseWithBind(
+        `<div id="foo" v-bind="bar" :class="cls" />`
+      )
+      expect(node.arguments.length).toBe(4)
+      expect(node.arguments[3]).toBe(String(PatchFlags.FULL_PROPS))
+    })
+
+    test('NEED_PATCH (static ref)', () => {
+      const { node } = parseWithBind(`<div ref="foo" />`)
+      expect(node.arguments.length).toBe(4)
+      expect(node.arguments[3]).toBe(String(PatchFlags.NEED_PATCH))
+    })
+
+    test('NEED_PATCH (dynamic ref)', () => {
+      const { node } = parseWithBind(`<div :ref="foo" />`)
+      expect(node.arguments.length).toBe(4)
+      expect(node.arguments[3]).toBe(String(PatchFlags.NEED_PATCH))
+    })
+
+    test('NEED_PATCH (custom directives)', () => {
+      const { node } = parseWithBind(`<div v-foo />`)
+      const vnodeCall = node.arguments[0] as CallExpression
+      expect(vnodeCall.arguments.length).toBe(4)
+      expect(vnodeCall.arguments[3]).toBe(String(PatchFlags.NEED_PATCH))
+    })
+  })
 })
index 748ba3496f41e5e833701c5f11a38abebd4f1464..bfd4a68ae88c807e8b27b7f016296778dc739ff7 100644 (file)
@@ -260,7 +260,7 @@ describe('compiler: transform component slots', () => {
                 type: NodeTypes.JS_CALL_EXPRESSION,
                 arguments: [
                   `_component_Inner`,
-                  `0`,
+                  `null`,
                   createSlotMatcher({
                     default: {
                       type: NodeTypes.JS_SLOT_FUNCTION,
index 30a3d6c07f48b072e52087260e7ce0ddfed25490..eb7ad3791473603e7749f356381c84cdd38251f0 100644 (file)
@@ -169,7 +169,7 @@ export type JSChildNode =
 
 export interface CallExpression extends Node {
   type: NodeTypes.JS_CALL_EXPRESSION
-  callee: string | ExpressionNode
+  callee: string
   arguments: (string | JSChildNode | ChildNode[])[]
 }
 
@@ -268,7 +268,7 @@ export function createCompoundExpression(
 }
 
 export function createCallExpression(
-  callee: string | ExpressionNode,
+  callee: string,
   args: CallExpression['arguments'],
   loc: SourceLocation
 ): CallExpression {
index ee0d5d2b6bb220ec8d60ff5fbd6a95dddb14df43..1b3f53c821a3b958a9db19332b6c2310790be549 100644 (file)
@@ -544,12 +544,7 @@ function genCallExpression(
   context: CodegenContext,
   multilines = node.arguments.length > 2
 ) {
-  if (isString(node.callee)) {
-    context.push(node.callee + `(`, node, true)
-  } else {
-    genNode(node.callee, context)
-    context.push(`(`)
-  }
+  context.push(node.callee + `(`, node, true)
   multilines && context.indent()
   genNodeList(node.arguments, context, multilines)
   multilines && context.deindent()
index 263c77dd7590057cd809e08d3f52e413c88ed618..ad0c512740b7feb3aa26edb789970b2bb2641dfe 100644 (file)
@@ -8,7 +8,8 @@ import {
   Property,
   ExpressionNode,
   createSimpleExpression,
-  JSChildNode
+  JSChildNode,
+  SimpleExpressionNode
 } from './ast'
 import { isString, isArray } from '@vue/shared'
 import { CompilerError, defaultOnError } from './errors'
@@ -65,7 +66,7 @@ export interface TransformContext extends Required<TransformOptions> {
   onNodeRemoved: () => void
   addIdentifiers(exp: ExpressionNode): void
   removeIdentifiers(exp: ExpressionNode): void
-  hoist(exp: JSChildNode): ExpressionNode
+  hoist(exp: JSChildNode): SimpleExpressionNode
 }
 
 function createTransformContext(
diff --git a/packages/compiler-core/src/transforms/optimizeBlocks.ts b/packages/compiler-core/src/transforms/optimizeBlocks.ts
deleted file mode 100644 (file)
index 70b786d..0000000
+++ /dev/null
@@ -1 +0,0 @@
-// TODO
diff --git a/packages/compiler-core/src/transforms/optimizePatchFlags.ts b/packages/compiler-core/src/transforms/optimizePatchFlags.ts
deleted file mode 100644 (file)
index 70b786d..0000000
+++ /dev/null
@@ -1 +0,0 @@
-// TODO
index 0f36d9fad6983103b3d94ccf0eacb55cec68a122..469f4e09d16d6ea6e269c812aa60e55ae8008fa5 100644 (file)
@@ -16,7 +16,7 @@ import {
   Property,
   SourceLocation
 } from '../ast'
-import { isArray } from '@vue/shared'
+import { isArray, PatchFlags } from '@vue/shared'
 import { createCompilerError, ErrorCodes } from '../errors'
 import {
   CREATE_VNODE,
@@ -41,7 +41,9 @@ export const transformElement: NodeTransform = (node, context) => {
       const isComponent = node.tagType === ElementTypes.COMPONENT
       let hasProps = node.props.length > 0
       const hasChildren = node.children.length > 0
+      let patchFlag: number = 0
       let runtimeDirectives: DirectiveNode[] | undefined
+      let dynamicPropNames: string[] | undefined
       let componentIdentifier: string | undefined
 
       if (isComponent) {
@@ -58,26 +60,51 @@ export const transformElement: NodeTransform = (node, context) => {
       ]
       // props
       if (hasProps) {
-        const { props, directives } = buildProps(
+        const propsBuildResult = buildProps(
           node.props,
           node.loc,
           context,
           isComponent
         )
-        runtimeDirectives = directives
-        if (!props) {
+        patchFlag = propsBuildResult.patchFlag
+        dynamicPropNames = propsBuildResult.dynamicPropNames
+        runtimeDirectives = propsBuildResult.directives
+        if (!propsBuildResult.props) {
           hasProps = false
         } else {
-          args.push(props)
+          args.push(propsBuildResult.props)
         }
       }
       // children
       if (hasChildren) {
         if (!hasProps) {
-          // placeholder for null props, but use `0` for more condense code
-          args.push(`0`)
+          args.push(`null`)
+        }
+        if (isComponent) {
+          const { slots, hasDynamicSlotName } = buildSlots(node, context)
+          args.push(slots)
+          if (hasDynamicSlotName) {
+            patchFlag |= PatchFlags.DYNAMIC_SLOTS
+          }
+        } else {
+          // only v-for fragments will have keyed/unkeyed flags
+          args.push(node.children)
+        }
+      }
+      // patchFlag & dynamicPropNames
+      if (patchFlag !== 0) {
+        if (!hasChildren) {
+          if (!hasProps) {
+            args.push(`null`)
+          }
+          args.push(`null`)
+        }
+        args.push(String(patchFlag))
+        if (dynamicPropNames && dynamicPropNames.length) {
+          args.push(
+            `[${dynamicPropNames.map(n => JSON.stringify(n)).join(`, `)}]`
+          )
         }
-        args.push(isComponent ? buildSlots(node, context) : node.children)
       }
 
       const { loc } = node
@@ -118,17 +145,30 @@ export function buildProps(
 ): {
   props: PropsExpression | undefined
   directives: DirectiveNode[]
+  patchFlag: number
+  dynamicPropNames: string[]
 } {
   let isStatic = true
   let properties: ObjectExpression['properties'] = []
   const mergeArgs: PropsExpression[] = []
   const runtimeDirectives: DirectiveNode[] = []
 
+  // patchFlag analysis
+  let patchFlag = 0
+  const dynamicPropNames: string[] = []
+  let hasDynammicKeys = false
+  let hasClassBinding = false
+  let hasStyleBinding = false
+  let hasRef = false
+
   for (let i = 0; i < props.length; i++) {
     // static attribute
     const prop = props[i]
     if (prop.type === NodeTypes.ATTRIBUTE) {
       const { loc, name, value } = prop
+      if (name === 'ref') {
+        hasRef = true
+      }
       properties.push(
         createObjectProperty(
           createSimpleExpression(
@@ -162,6 +202,7 @@ export function buildProps(
       // special case for v-bind and v-on with no argument
       const isBind = name === 'bind'
       if (!arg && (isBind || name === 'on')) {
+        hasDynammicKeys = true
         if (exp) {
           if (properties.length) {
             mergeArgs.push(
@@ -193,6 +234,24 @@ export function buildProps(
         continue
       }
 
+      // patchFlag analysis
+      if (isBind && arg) {
+        if (arg.type === NodeTypes.SIMPLE_EXPRESSION && arg.isStatic) {
+          const name = arg.content
+          if (name === 'ref') {
+            hasRef = true
+          } else if (name === 'class') {
+            hasClassBinding = true
+          } else if (name === 'style') {
+            hasStyleBinding = true
+          } else {
+            dynamicPropNames.push(name)
+          }
+        } else {
+          hasDynammicKeys = true
+        }
+      }
+
       const directiveTransform = context.directiveTransforms[name]
       if (directiveTransform) {
         // has built-in directive transform.
@@ -243,9 +302,29 @@ export function buildProps(
     propsExpression = context.hoist(propsExpression)
   }
 
+  // determine the flags to add
+  if (hasDynammicKeys) {
+    patchFlag |= PatchFlags.FULL_PROPS
+  } else {
+    if (hasClassBinding) {
+      patchFlag |= PatchFlags.CLASS
+    }
+    if (hasStyleBinding) {
+      patchFlag |= PatchFlags.STYLE
+    }
+    if (dynamicPropNames.length) {
+      patchFlag |= PatchFlags.PROPS
+    }
+  }
+  if (patchFlag === 0 && (hasRef || runtimeDirectives.length > 0)) {
+    patchFlag |= PatchFlags.NEED_PATCH
+  }
+
   return {
     props: propsExpression,
-    directives: runtimeDirectives
+    directives: runtimeDirectives,
+    patchFlag,
+    dynamicPropNames
   }
 }
 
index 44cadf23e0f4426e704e5d2f1844ee5dbe3db595..eb9fd12bb6c87ed44f8476b5938d60a1529b3378 100644 (file)
@@ -44,8 +44,12 @@ export const trackSlotScopes: NodeTransform = (node, context) => {
 export function buildSlots(
   { props, children, loc }: ElementNode,
   context: TransformContext
-): ObjectExpression {
+): {
+  slots: ObjectExpression
+  hasDynamicSlotName: boolean
+} {
   const slots: Property[] = []
+  let hasDynamicSlotName = false
 
   // 1. Check for default slot with slotProps on component itself.
   //    <Comp v-slot="{ prop }"/>
@@ -83,11 +87,11 @@ export function buildSlots(
         )
         break
       } else {
-        // check duplicate slot names
         if (
           !slotName ||
           (slotName.type === NodeTypes.SIMPLE_EXPRESSION && slotName.isStatic)
         ) {
+          // check duplicate slot names
           const name = slotName ? slotName.content : `default`
           if (seenSlotNames.has(name)) {
             context.onError(
@@ -96,6 +100,8 @@ export function buildSlots(
             continue
           }
           seenSlotNames.add(name)
+        } else {
+          hasDynamicSlotName = true
         }
         slots.push(
           buildSlot(slotName || `default`, slotProps, children, nodeLoc)
@@ -120,7 +126,10 @@ export function buildSlots(
     slots.push(buildSlot(`default`, undefined, children, loc))
   }
 
-  return createObjectExpression(slots, loc)
+  return {
+    slots: createObjectExpression(slots, loc),
+    hasDynamicSlotName
+  }
 }
 
 function buildSlot(
index 0be57da3254112cefaaf26ce06a75b66dbdce5b1..a550de7207b2675043e0c55aa7c683ff0829d143 100644 (file)
@@ -107,7 +107,7 @@ describe('renderer: fragment', () => {
   it('patch fragment children (compiler generated, unkeyed)', () => {
     const root = nodeOps.createElement('div')
     render(
-      createVNode(Fragment, 0, [h('div', 'one'), 'two'], PatchFlags.UNKEYED),
+      createVNode(Fragment, null, [h('div', 'one'), 'two'], PatchFlags.UNKEYED),
       root
     )
     expect(serializeInner(root)).toBe(`<!----><div>one</div>two<!---->`)
@@ -115,7 +115,7 @@ describe('renderer: fragment', () => {
     render(
       createVNode(
         Fragment,
-        0,
+        null,
         [h('div', 'foo'), 'bar', 'baz'],
         PatchFlags.UNKEYED
       ),
@@ -130,7 +130,7 @@ describe('renderer: fragment', () => {
     render(
       createVNode(
         Fragment,
-        0,
+        null,
         [h('div', { key: 1 }, 'one'), h('div', { key: 2 }, 'two')],
         PatchFlags.KEYED
       ),
@@ -144,7 +144,7 @@ describe('renderer: fragment', () => {
     render(
       createVNode(
         Fragment,
-        0,
+        null,
         [h('div', { key: 2 }, 'two'), h('div', { key: 1 }, 'one')],
         PatchFlags.KEYED
       ),
index 2ed15514f177478898c06a88cca523cf188285bc..fad4d78b7e4278a1512f498952601e9c1dc8d21c 100644 (file)
@@ -10,11 +10,11 @@ import {
   isObject,
   isReservedProp,
   hasOwn,
-  toTypeString
+  toTypeString,
+  PatchFlags
 } from '@vue/shared'
 import { warn } from './warning'
 import { Data, ComponentInternalInstance } from './component'
-import { PatchFlags } from './patchFlags'
 
 export type ComponentPropsOptions<P = Data> = {
   [K in keyof P]: Prop<P[K]> | null
index 0a617effcf726cf8858367be9c01f5858d1aa70c..0f9caab641f432065d6adc3b2c112ed41f1d62f4 100644 (file)
@@ -6,7 +6,7 @@ import {
 import { VNode, normalizeVNode, createVNode, Comment } from './vnode'
 import { ShapeFlags } from './shapeFlags'
 import { handleError, ErrorCodes } from './errorHandling'
-import { PatchFlags } from './patchFlags'
+import { PatchFlags } from '@vue/shared'
 
 // mark the current rendering instance for asset resolution (e.g.
 // resolveComponent, resolveDirective) during render
index 90fb8524c93d94b8d3d544c8547e98118cb503f7..a7dc9be6a641331bb45217dd64823e8a33dc2182 100644 (file)
@@ -25,7 +25,8 @@ import {
   EMPTY_ARR,
   isReservedProp,
   isFunction,
-  isArray
+  isArray,
+  PatchFlags
 } from '@vue/shared'
 import { queueJob, queuePostFlushCb, flushPostFlushCbs } from './scheduler'
 import {
@@ -38,7 +39,6 @@ import {
 } from '@vue/reactivity'
 import { resolveProps } from './componentProps'
 import { resolveSlots } from './componentSlots'
-import { PatchFlags } from './patchFlags'
 import { ShapeFlags } from './shapeFlags'
 import { pushWarningContext, popWarningContext, warn } from './warning'
 import { invokeDirectiveHook } from './directives'
index 6dbbbb85d29149ea60f787920e18f3db463cf127..9a7198d249cb5152fffbe7a1cb4346a1e3a5075e 100644 (file)
@@ -21,8 +21,8 @@ export {
 // VNode type symbols
 export { Text, Comment, Fragment, Portal, Suspense } from './vnode'
 // VNode flags
-export { PublicPatchFlags as PatchFlags } from './patchFlags'
 export { PublicShapeFlags as ShapeFlags } from './shapeFlags'
+export { PublicPatchFlags as PatchFlags } from '@vue/shared'
 
 // For advanced plugins
 export { getCurrentInstance } from './component'
index b5b686d81a57a2e476aacb1407d09fc544304c7d..f0090ffb87fa7b2727146c1d9604aa0b75cbda89 100644 (file)
@@ -4,11 +4,11 @@ import {
   isString,
   isObject,
   EMPTY_ARR,
-  extend
+  extend,
+  PatchFlags
 } from '@vue/shared'
 import { ComponentInternalInstance, Data, SetupProxySymbol } from './component'
 import { RawSlots } from './componentSlots'
-import { PatchFlags } from './patchFlags'
 import { ShapeFlags } from './shapeFlags'
 import { isReactive } from '@vue/reactivity'
 import { AppContext } from './apiApp'
@@ -131,14 +131,11 @@ export function isVNode(value: any): boolean {
 
 export function createVNode(
   type: VNodeTypes,
-  props: { [key: string]: any } | null | 0 = null,
-  children: any = null,
+  props: { [key: string]: any } | null = null,
+  children: unknown = null,
   patchFlag: number = 0,
   dynamicProps: string[] | null = null
 ): VNode {
-  // Allow passing 0 for props, this can save bytes on generated code.
-  props = props || null
-
   // class & style normalization.
   if (props !== null) {
     // for reactive or proxy objects, we need to clone it to enable mutation.
index 211c72d3d02963c996b40d10c2c330c2f6f228b2..93781a8d229454e495a765f32d52b68f48083c95 100644 (file)
@@ -1,3 +1,5 @@
+export * from './patchFlags'
+
 export const EMPTY_OBJ: { readonly [key: string]: any } = __DEV__
   ? Object.freeze({})
   : {}
similarity index 90%
rename from packages/runtime-core/src/patchFlags.ts
rename to packages/shared/src/patchFlags.ts
index 9061107c7d73aa85e8a2d0240c804e0165f186b3..13336c2210ec6c2b730f973ce4e68b45ea05f0ba 100644 (file)
@@ -17,12 +17,7 @@ export const enum PatchFlags {
   // Indicates an element with dynamic textContent (children fast path)
   TEXT = 1,
 
-  // Indicates an element with dynamic class.
-  // The compiler also pre-normalizes the :class binding:
-  // - b -> normalize(b)
-  // - ['foo', b] -> 'foo' + normalize(b)
-  // - { a, b: c } -> (a ? a : '') + (b ? c : '')
-  // - ['a', b, { c }] -> 'a' + normalize(b) + (c ? c : '')
+  // Indicates an element with dynamic class binding.
   CLASS = 1 << 1,
 
   // Indicates an element with dynamic style
@@ -48,6 +43,8 @@ export const enum PatchFlags {
   // Indicates an element that only needs non-props patching, e.g. ref or
   // directives (vnodeXXX hooks). It simply marks the vnode as "need patch",
   // since every pathced vnode checks for refs and vnodeXXX hooks.
+  // This flag is never directly matched against, it simply serves as a non-zero
+  // value.
   NEED_PATCH = 1 << 5,
 
   // Indicates a fragment or element with keyed or partially-keyed v-for