]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
perf: micro optimizations for vnode creation
authorEvan You <yyx990803@gmail.com>
Mon, 21 Oct 2019 15:30:45 +0000 (11:30 -0400)
committerEvan You <yyx990803@gmail.com>
Mon, 21 Oct 2019 15:30:45 +0000 (11:30 -0400)
packages/runtime-core/src/vnode.ts

index 5c5455ff82fc47995a7d1df8cf1d181d9781efb9..632b160def3b6542b4f11edd9353bc15d9c02ac5 100644 (file)
@@ -88,6 +88,7 @@ export interface VNode<HostNode = any, HostElement = any> {
 // structure would be stable. This allows us to skip most children diffing
 // and only worry about the dynamic nodes (indicated by patch flags).
 const blockStack: (VNode[] | null)[] = []
+let currentBlock: VNode[] | null = null
 
 // Open a block.
 // This must be called before `createBlock`. It cannot be part of `createBlock`
@@ -101,7 +102,7 @@ const blockStack: (VNode[] | null)[] = []
 // disableTracking is true when creating a fragment block, since a fragment
 // always diffs its children.
 export function openBlock(disableTracking?: boolean) {
-  blockStack.push(disableTracking ? null : [])
+  blockStack.push((currentBlock = disableTracking ? null : []))
 }
 
 let shouldTrack = true
@@ -116,15 +117,20 @@ export function createBlock(
   patchFlag?: number,
   dynamicProps?: string[]
 ): VNode {
-  // avoid a block with optFlag tracking itself
+  // avoid a block with patchFlag tracking itself
   shouldTrack = false
   const vnode = createVNode(type, props, children, patchFlag, dynamicProps)
   shouldTrack = true
-  const trackedNodes = blockStack.pop()
-  vnode.dynamicChildren =
-    trackedNodes && trackedNodes.length ? trackedNodes : EMPTY_ARR
-  // a block is always going to be patched
-  trackDynamicNode(vnode)
+  // save current block children on the block vnode
+  vnode.dynamicChildren = currentBlock || EMPTY_ARR
+  // close block
+  blockStack.pop()
+  currentBlock = blockStack[blockStack.length - 1] || null
+  // a block is always going to be patched, so track it as a child of its
+  // parent block
+  if (currentBlock !== null) {
+    currentBlock.push(vnode)
+  }
   return vnode
 }
 
@@ -145,10 +151,10 @@ export function createVNode(
     if (isReactive(props) || SetupProxySymbol in props) {
       props = extend({}, props)
     }
-    if (props.class != null) {
-      props.class = normalizeClass(props.class)
+    let { class: klass, style } = props
+    if (klass != null && !isString(klass)) {
+      props.class = normalizeClass(klass)
     }
-    let { style } = props
     if (style != null) {
       // reactive state objects need to be cloned since they are likely to be
       // mutated
@@ -172,8 +178,8 @@ export function createVNode(
     _isVNode: true,
     type,
     props,
-    key: (props && props.key) || null,
-    ref: (props && props.ref) || null,
+    key: (props !== null && props.key) || null,
+    ref: (props !== null && props.ref) || null,
     children: null,
     component: null,
     suspense: null,
@@ -195,23 +201,17 @@ export function createVNode(
   // the next vnode so that it can be properly unmounted later.
   if (
     shouldTrack &&
-    (patchFlag ||
+    currentBlock !== null &&
+    (patchFlag > 0 ||
       shapeFlag & ShapeFlags.STATEFUL_COMPONENT ||
       shapeFlag & ShapeFlags.FUNCTIONAL_COMPONENT)
   ) {
-    trackDynamicNode(vnode)
+    currentBlock.push(vnode)
   }
 
   return vnode
 }
 
-function trackDynamicNode(vnode: VNode) {
-  const currentBlockDynamicNodes = blockStack[blockStack.length - 1]
-  if (currentBlockDynamicNodes != null) {
-    currentBlockDynamicNodes.push(vnode)
-  }
-}
-
 export function cloneVNode(vnode: VNode): VNode {
   return {
     _isVNode: true,