]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
build: generate more treeshaking friendly code
authorEvan You <yyx990803@gmail.com>
Thu, 16 Sep 2021 14:56:34 +0000 (10:56 -0400)
committerEvan You <yyx990803@gmail.com>
Thu, 16 Sep 2021 14:56:34 +0000 (10:56 -0400)
packages/runtime-core/src/componentRenderUtils.ts
packages/runtime-core/src/scheduler.ts

index 79cafc4205c3a1bf4822cdbb6d21ca116be3051d..ce03b4f6af055f410228f82dc6e90673f85229df 100644 (file)
@@ -60,12 +60,13 @@ export function renderComponentRoot(
   } = instance
 
   let result
+  let fallthroughAttrs
   const prev = setCurrentRenderingInstance(instance)
   if (__DEV__) {
     accessedAttrs = false
   }
+
   try {
-    let fallthroughAttrs
     if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
       // withProxy is a proxy with a different `has` trap only for
       // runtime-compiled render functions using `with` block.
@@ -110,127 +111,127 @@ export function renderComponentRoot(
         ? attrs
         : getFunctionalFallthrough(attrs)
     }
+  } catch (err) {
+    blockStack.length = 0
+    handleError(err, instance, ErrorCodes.RENDER_FUNCTION)
+    result = createVNode(Comment)
+  }
 
-    // attr merging
-    // in dev mode, comments are preserved, and it's possible for a template
-    // to have comments along side the root element which makes it a fragment
-    let root = result
-    let setRoot: ((root: VNode) => void) | undefined = undefined
-    if (
-      __DEV__ &&
-      result.patchFlag > 0 &&
-      result.patchFlag & PatchFlags.DEV_ROOT_FRAGMENT
-    ) {
-      ;[root, setRoot] = getChildRoot(result)
-    }
+  // attr merging
+  // in dev mode, comments are preserved, and it's possible for a template
+  // to have comments along side the root element which makes it a fragment
+  let root = result
+  let setRoot: ((root: VNode) => void) | undefined = undefined
+  if (
+    __DEV__ &&
+    result.patchFlag > 0 &&
+    result.patchFlag & PatchFlags.DEV_ROOT_FRAGMENT
+  ) {
+    ;[root, setRoot] = getChildRoot(result)
+  }
 
-    if (fallthroughAttrs && inheritAttrs !== false) {
-      const keys = Object.keys(fallthroughAttrs)
-      const { shapeFlag } = root
-      if (keys.length) {
-        if (shapeFlag & (ShapeFlags.ELEMENT | ShapeFlags.COMPONENT)) {
-          if (propsOptions && keys.some(isModelListener)) {
-            // If a v-model listener (onUpdate:xxx) has a corresponding declared
-            // prop, it indicates this component expects to handle v-model and
-            // it should not fallthrough.
-            // related: #1543, #1643, #1989
-            fallthroughAttrs = filterModelListeners(
-              fallthroughAttrs,
-              propsOptions
-            )
-          }
-          root = cloneVNode(root, fallthroughAttrs)
-        } else if (__DEV__ && !accessedAttrs && root.type !== Comment) {
-          const allAttrs = Object.keys(attrs)
-          const eventAttrs: string[] = []
-          const extraAttrs: string[] = []
-          for (let i = 0, l = allAttrs.length; i < l; i++) {
-            const key = allAttrs[i]
-            if (isOn(key)) {
-              // ignore v-model handlers when they fail to fallthrough
-              if (!isModelListener(key)) {
-                // remove `on`, lowercase first letter to reflect event casing
-                // accurately
-                eventAttrs.push(key[2].toLowerCase() + key.slice(3))
-              }
-            } else {
-              extraAttrs.push(key)
+  if (fallthroughAttrs && inheritAttrs !== false) {
+    const keys = Object.keys(fallthroughAttrs)
+    const { shapeFlag } = root
+    if (keys.length) {
+      if (shapeFlag & (ShapeFlags.ELEMENT | ShapeFlags.COMPONENT)) {
+        if (propsOptions && keys.some(isModelListener)) {
+          // If a v-model listener (onUpdate:xxx) has a corresponding declared
+          // prop, it indicates this component expects to handle v-model and
+          // it should not fallthrough.
+          // related: #1543, #1643, #1989
+          fallthroughAttrs = filterModelListeners(
+            fallthroughAttrs,
+            propsOptions
+          )
+        }
+        root = cloneVNode(root, fallthroughAttrs)
+      } else if (__DEV__ && !accessedAttrs && root.type !== Comment) {
+        const allAttrs = Object.keys(attrs)
+        const eventAttrs: string[] = []
+        const extraAttrs: string[] = []
+        for (let i = 0, l = allAttrs.length; i < l; i++) {
+          const key = allAttrs[i]
+          if (isOn(key)) {
+            // ignore v-model handlers when they fail to fallthrough
+            if (!isModelListener(key)) {
+              // remove `on`, lowercase first letter to reflect event casing
+              // accurately
+              eventAttrs.push(key[2].toLowerCase() + key.slice(3))
             }
-          }
-          if (extraAttrs.length) {
-            warn(
-              `Extraneous non-props attributes (` +
-                `${extraAttrs.join(', ')}) ` +
-                `were passed to component but could not be automatically inherited ` +
-                `because component renders fragment or text root nodes.`
-            )
-          }
-          if (eventAttrs.length) {
-            warn(
-              `Extraneous non-emits event listeners (` +
-                `${eventAttrs.join(', ')}) ` +
-                `were passed to component but could not be automatically inherited ` +
-                `because component renders fragment or text root nodes. ` +
-                `If the listener is intended to be a component custom event listener only, ` +
-                `declare it using the "emits" option.`
-            )
+          } else {
+            extraAttrs.push(key)
           }
         }
-      }
-    }
-
-    if (
-      __COMPAT__ &&
-      isCompatEnabled(DeprecationTypes.INSTANCE_ATTRS_CLASS_STYLE, instance) &&
-      vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT &&
-      root.shapeFlag & (ShapeFlags.ELEMENT | ShapeFlags.COMPONENT)
-    ) {
-      const { class: cls, style } = vnode.props || {}
-      if (cls || style) {
-        if (__DEV__ && inheritAttrs === false) {
-          warnDeprecation(
-            DeprecationTypes.INSTANCE_ATTRS_CLASS_STYLE,
-            instance,
-            getComponentName(instance.type)
+        if (extraAttrs.length) {
+          warn(
+            `Extraneous non-props attributes (` +
+              `${extraAttrs.join(', ')}) ` +
+              `were passed to component but could not be automatically inherited ` +
+              `because component renders fragment or text root nodes.`
+          )
+        }
+        if (eventAttrs.length) {
+          warn(
+            `Extraneous non-emits event listeners (` +
+              `${eventAttrs.join(', ')}) ` +
+              `were passed to component but could not be automatically inherited ` +
+              `because component renders fragment or text root nodes. ` +
+              `If the listener is intended to be a component custom event listener only, ` +
+              `declare it using the "emits" option.`
           )
         }
-        root = cloneVNode(root, {
-          class: cls,
-          style: style
-        })
       }
     }
+  }
 
-    // inherit directives
-    if (vnode.dirs) {
-      if (__DEV__ && !isElementRoot(root)) {
-        warn(
-          `Runtime directive used on component with non-element root node. ` +
-            `The directives will not function as intended.`
+  if (
+    __COMPAT__ &&
+    isCompatEnabled(DeprecationTypes.INSTANCE_ATTRS_CLASS_STYLE, instance) &&
+    vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT &&
+    root.shapeFlag & (ShapeFlags.ELEMENT | ShapeFlags.COMPONENT)
+  ) {
+    const { class: cls, style } = vnode.props || {}
+    if (cls || style) {
+      if (__DEV__ && inheritAttrs === false) {
+        warnDeprecation(
+          DeprecationTypes.INSTANCE_ATTRS_CLASS_STYLE,
+          instance,
+          getComponentName(instance.type)
         )
       }
-      root.dirs = root.dirs ? root.dirs.concat(vnode.dirs) : vnode.dirs
-    }
-    // inherit transition data
-    if (vnode.transition) {
-      if (__DEV__ && !isElementRoot(root)) {
-        warn(
-          `Component inside <Transition> renders non-element root node ` +
-            `that cannot be animated.`
-        )
-      }
-      root.transition = vnode.transition
+      root = cloneVNode(root, {
+        class: cls,
+        style: style
+      })
     }
+  }
 
-    if (__DEV__ && setRoot) {
-      setRoot(root)
-    } else {
-      result = root
+  // inherit directives
+  if (vnode.dirs) {
+    if (__DEV__ && !isElementRoot(root)) {
+      warn(
+        `Runtime directive used on component with non-element root node. ` +
+          `The directives will not function as intended.`
+      )
     }
-  } catch (err) {
-    blockStack.length = 0
-    handleError(err, instance, ErrorCodes.RENDER_FUNCTION)
-    result = createVNode(Comment)
+    root.dirs = root.dirs ? root.dirs.concat(vnode.dirs) : vnode.dirs
+  }
+  // inherit transition data
+  if (vnode.transition) {
+    if (__DEV__ && !isElementRoot(root)) {
+      warn(
+        `Component inside <Transition> renders non-element root node ` +
+          `that cannot be animated.`
+      )
+    }
+    root.transition = vnode.transition
+  }
+
+  if (__DEV__ && setRoot) {
+    setRoot(root)
+  } else {
+    result = root
   }
 
   setCurrentRenderingInstance(prev)
index 333125d578ddc5f478c6aa65e8d3d509250ed955..ccf7ac70b417537bef075bcd84ac8e0eb3adf013 100644 (file)
@@ -1,5 +1,5 @@
 import { ErrorCodes, callWithErrorHandling } from './errorHandling'
-import { isArray } from '@vue/shared'
+import { isArray, NOOP } from '@vue/shared'
 import { ComponentInternalInstance, getComponentName } from './component'
 import { warn } from './warning'
 
@@ -128,10 +128,7 @@ function queueCb(
   if (!isArray(cb)) {
     if (
       !activeQueue ||
-      !activeQueue.includes(
-        cb,
-        cb.allowRecurse ? index + 1 : index
-      )
+      !activeQueue.includes(cb, cb.allowRecurse ? index + 1 : index)
     ) {
       pendingQueue.push(cb)
     }
@@ -241,11 +238,20 @@ function flushJobs(seen?: CountMap) {
   //    its update can be skipped.
   queue.sort((a, b) => getId(a) - getId(b))
 
+  // conditional usage of checkRecursiveUpdate must be determined out of
+  // try ... catch block since Rollup by default de-optimizes treeshaking
+  // inside try-catch. This can leave all warning code unshaked. Although
+  // they would get eventually shaken by a minifier like terser, some minifiers
+  // would fail to do that (e.g. https://github.com/evanw/esbuild/issues/1610)
+  const check = __DEV__
+    ? (job: SchedulerJob) => checkRecursiveUpdates(seen!, job)
+    : NOOP
+
   try {
     for (flushIndex = 0; flushIndex < queue.length; flushIndex++) {
       const job = queue[flushIndex]
       if (job && job.active !== false) {
-        if (__DEV__ && checkRecursiveUpdates(seen!, job)) {
+        if (__DEV__ && check(job)) {
           continue
         }
         // console.log(`running:`, job.id)