]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor: proper options inheritance
authorEvan You <yyx990803@gmail.com>
Tue, 16 Oct 2018 23:10:08 +0000 (19:10 -0400)
committerEvan You <yyx990803@gmail.com>
Tue, 16 Oct 2018 23:10:08 +0000 (19:10 -0400)
packages/core/src/componentOptions.ts
packages/core/src/componentState.ts
packages/core/src/componentUtils.ts
packages/core/src/createRenderer.ts
packages/core/src/h.ts

index 2b033940a99b9d3c9e15599a1d96288935b4362b..385a2f6de86900744b5aa88df540ef72de009f67 100644 (file)
@@ -1,4 +1,5 @@
 import {
+  Component,
   ComponentInstance,
   ComponentClass,
   APIMethods,
@@ -104,22 +105,22 @@ export const reservedMethods: ReservedKeys = {
 // This is called in the base component constructor and the return value is
 // set on the instance as $options.
 export function resolveComponentOptionsFromClass(
-  Component: ComponentClass
+  Class: ComponentClass
 ): ComponentOptions {
-  if (Component.options) {
-    return Component.options
+  if (Class.hasOwnProperty('options')) {
+    return Class.options as ComponentOptions
   }
-  const staticDescriptors = Object.getOwnPropertyDescriptors(Component)
-  const options = {} as any
+  let options = {} as any
+
+  const staticDescriptors = Object.getOwnPropertyDescriptors(Class)
   for (const key in staticDescriptors) {
     const { enumerable, get, value } = staticDescriptors[key]
     if (enumerable || get) {
       options[key] = get ? get() : value
     }
   }
-  const instanceDescriptors = Object.getOwnPropertyDescriptors(
-    Component.prototype
-  )
+
+  const instanceDescriptors = Object.getOwnPropertyDescriptors(Class.prototype)
   for (const key in instanceDescriptors) {
     const { get, value } = instanceDescriptors[key]
     if (get) {
@@ -127,7 +128,7 @@ export function resolveComponentOptionsFromClass(
       ;(options.computed || (options.computed = {}))[key] = get
       // there's no need to do anything for the setter
       // as it's already defined on the prototype
-    } else if (isFunction(value)) {
+    } else if (isFunction(value) && key !== 'constructor') {
       if (key in reservedMethods) {
         options[key] = value
       } else {
@@ -135,8 +136,16 @@ export function resolveComponentOptionsFromClass(
       }
     }
   }
+
   options.props = normalizePropsOptions(options.props)
-  Component.options = options
+
+  const ParentClass = Object.getPrototypeOf(Class)
+  if (ParentClass !== Component) {
+    const parentOptions = resolveComponentOptionsFromClass(ParentClass)
+    options = mergeComponentOptions(parentOptions, options)
+  }
+
+  Class.options = options
   return options
 }
 
@@ -154,7 +163,7 @@ export function mergeComponentOptions(to: any, from: any): ComponentOptions {
       if (key === 'data') {
         // for data we need to merge the returned value
         res[key] = function() {
-          return Object.assign(existing(), value())
+          return Object.assign(existing.call(this), value.call(this))
         }
       } else if (/^render|^errorCaptured/.test(key)) {
         // render, renderTracked, renderTriggered & errorCaptured
index 5c1477ce6a7f8eb8c2deaac82966a3e4cfef558d..594d8b3d3c0e63d65a64763d6a0c904805419866 100644 (file)
@@ -4,17 +4,14 @@ import { observable } from '@vue/observer'
 const internalRE = /^_|^\$/
 
 export function initializeState(instance: ComponentInstance) {
-  if (instance.data) {
-    instance._rawData = instance.data()
-  } else {
-    const keys = Object.keys(instance)
-    const data = (instance._rawData = {} as any)
-    for (let i = 0; i < keys.length; i++) {
-      const key = keys[i]
-      if (!internalRE.test(key)) {
-        data[key] = (instance as any)[key]
-      }
+  const { data } = instance.$options
+  const rawData = (instance._rawData = (data ? data.call(instance) : {}) as any)
+  const keys = Object.keys(instance)
+  for (let i = 0; i < keys.length; i++) {
+    const key = keys[i]
+    if (!internalRE.test(key)) {
+      rawData[key] = (instance as any)[key]
     }
   }
-  instance.$data = observable(instance._rawData || {})
+  instance.$data = observable(rawData || {})
 }
index 3eb69f5a94ca9cf263659471a5808bb5ab949df8..76a1e3e0a91375db7efc055fc2784e05bda33559 100644 (file)
@@ -38,15 +38,22 @@ export function createComponentInstance(
   currentVNode = vnode
   currentContextVNode = vnode.contextVNode
   const instance = (vnode.children = new Component() as ComponentInstance)
+
   // then we finish the initialization by collecting properties set on the
   // instance
+  const {
+    $proxy,
+    $options: { created, computed, watch }
+  } = instance
   initializeState(instance)
-  initializeComputed(instance, instance.$options.computed)
-  initializeWatch(instance, instance.$options.watch)
+  initializeComputed(instance, computed)
+  initializeWatch(instance, watch)
   instance.$slots = currentVNode.slots || EMPTY_OBJ
-  if (instance.created) {
-    instance.created.call(instance.$proxy)
+
+  if (created) {
+    created.call($proxy)
   }
+
   currentVNode = currentContextVNode = null
   return instance
 }
@@ -87,14 +94,11 @@ export function initializeComponentInstance(instance: ComponentInstance) {
   }
 
   // beforeCreate hook is called right in the constructor
-  if (instance.beforeCreate) {
-    instance.beforeCreate.call(proxy)
+  const { beforeCreate, props } = instance.$options
+  if (beforeCreate) {
+    beforeCreate.call(proxy)
   }
-  initializeProps(
-    instance,
-    instance.$options.props,
-    (currentVNode as VNode).data
-  )
+  initializeProps(instance, props, (currentVNode as VNode).data)
 }
 
 export function renderInstanceRoot(instance: ComponentInstance): VNode {
index 8ffffca35a4127039d8f38566364d08c7ead7162..963432e04f5542f67ec97c3770b5b924572bb353 100644 (file)
@@ -1167,8 +1167,13 @@ export function createRenderer(options: RendererOptions) {
       ;(instance as any).$unmount = unmountComponentInstance
     }
 
-    if (instance.beforeMount) {
-      instance.beforeMount.call(instance.$proxy)
+    const {
+      $proxy,
+      $options: { beforeMount, mounted, renderTracked, renderTriggered }
+    } = instance
+
+    if (beforeMount) {
+      beforeMount.call($proxy)
     }
 
     const queueUpdate = (instance.$forceUpdate = () => {
@@ -1200,33 +1205,26 @@ export function createRenderer(options: RendererOptions) {
           }
 
           instance._mounted = true
-          mountComponentInstanceCallbacks(instance, vnode.ref)
+          if (vnode.ref) {
+            mountRef(vnode.ref, $proxy)
+          }
+          if (mounted) {
+            lifecycleHooks.push(() => {
+              mounted.call($proxy)
+            })
+          }
         }
       },
       {
         scheduler: queueUpdate,
-        onTrack: instance.renderTracked,
-        onTrigger: instance.renderTriggered
+        onTrack: renderTracked,
+        onTrigger: renderTriggered
       }
     )
 
     return vnode.el as RenderNode
   }
 
-  function mountComponentInstanceCallbacks(
-    instance: ComponentInstance,
-    ref: Ref | null
-  ) {
-    if (ref) {
-      mountRef(ref, instance.$proxy)
-    }
-    if (instance.mounted) {
-      lifecycleHooks.push(() => {
-        ;(instance as any).mounted.call(instance.$proxy)
-      })
-    }
-  }
-
   function updateComponentInstance(
     instance: ComponentInstance,
     isSVG: boolean
@@ -1234,23 +1232,22 @@ export function createRenderer(options: RendererOptions) {
     if (__DEV__ && instance.$parentVNode) {
       pushWarningContext(instance.$parentVNode as VNode)
     }
-    const prevVNode = instance.$vnode
 
-    if (instance.beforeUpdate) {
-      instance.beforeUpdate.call(instance.$proxy, prevVNode)
+    const {
+      $vnode: prevVNode,
+      $parentVNode,
+      $proxy,
+      $options: { beforeUpdate, updated }
+    } = instance
+    if (beforeUpdate) {
+      beforeUpdate.call($proxy, prevVNode)
     }
 
     const nextVNode = (instance.$vnode = renderInstanceRoot(
       instance
     ) as MountedVNode)
     const container = platformParentNode(prevVNode.el) as RenderNode
-    patch(
-      prevVNode,
-      nextVNode,
-      container,
-      instance.$parentVNode as MountedVNode,
-      isSVG
-    )
+    patch(prevVNode, nextVNode, container, $parentVNode as MountedVNode, isSVG)
     const el = nextVNode.el as RenderNode
 
     if (__COMPAT__) {
@@ -1260,7 +1257,7 @@ export function createRenderer(options: RendererOptions) {
 
     // recursively update contextVNode el for nested HOCs
     if ((nextVNode.flags & VNodeFlags.PORTAL) === 0) {
-      let vnode = instance.$parentVNode
+      let vnode = $parentVNode
       while (vnode !== null) {
         if ((vnode.flags & VNodeFlags.COMPONENT) > 0) {
           vnode.el = el
@@ -1269,14 +1266,14 @@ export function createRenderer(options: RendererOptions) {
       }
     }
 
-    if (instance.updated) {
+    if (updated) {
       // Because the child's update is executed by the scheduler and not
       // synchronously within the parent's update call, the child's updated hook
       // will be added to the queue AFTER the parent's, but they should be
       // invoked BEFORE the parent's. Therefore we add them to the head of the
       // queue instead.
       lifecycleHooks.unshift(() => {
-        ;(instance as any).updated.call(instance.$proxy, nextVNode)
+        updated.call($proxy, nextVNode)
       })
     }
 
@@ -1299,17 +1296,23 @@ export function createRenderer(options: RendererOptions) {
     if (instance._unmounted) {
       return
     }
-    if (instance.beforeUnmount) {
-      instance.beforeUnmount.call(instance.$proxy)
-    }
-    if (instance.$vnode) {
-      unmount(instance.$vnode)
-    }
-    stop(instance._updateHandle)
+    const {
+      $vnode,
+      $proxy,
+      _updateHandle,
+      $options: { beforeUnmount, unmounted }
+    } = instance
+    if (beforeUnmount) {
+      beforeUnmount.call($proxy)
+    }
+    if ($vnode) {
+      unmount($vnode)
+    }
+    stop(_updateHandle)
     teardownComponentInstance(instance)
     instance._unmounted = true
-    if (instance.unmounted) {
-      instance.unmounted.call(instance.$proxy)
+    if (unmounted) {
+      unmounted.call($proxy)
     }
   }
 
@@ -1338,12 +1341,16 @@ export function createRenderer(options: RendererOptions) {
     }
     if (asRoot || !instance._inactiveRoot) {
       // 2. recursively call activated on child tree, depth-first
-      const { $children } = instance
+      const {
+        $children,
+        $proxy,
+        $options: { activated }
+      } = instance
       for (let i = 0; i < $children.length; i++) {
         callActivatedHook($children[i], false)
       }
-      if (instance.activated) {
-        instance.activated.call(instance.$proxy)
+      if (activated) {
+        activated.call($proxy)
       }
     }
   }
@@ -1359,12 +1366,16 @@ export function createRenderer(options: RendererOptions) {
     }
     if (asRoot || !instance._inactiveRoot) {
       // 2. recursively call deactivated on child tree, depth-first
-      const { $children } = instance
+      const {
+        $children,
+        $proxy,
+        $options: { deactivated }
+      } = instance
       for (let i = 0; i < $children.length; i++) {
         callDeactivateHook($children[i], false)
       }
-      if (instance.deactivated) {
-        instance.deactivated.call(instance.$proxy)
+      if (deactivated) {
+        deactivated.call($proxy)
       }
     }
   }
index f7323f31ebbe1fbfc6da318d94e145910c07d61a..86ab1846a82bbb1435cf53a336c5d73c18420759 100644 (file)
@@ -99,7 +99,7 @@ interface createElement extends VNodeFactories {
 }
 
 export const h = ((tag: ElementType, data?: any, children?: any): VNode => {
-  if (isArray(data) || !isObject(data) || data._isVNode) {
+  if (data !== null && (isArray(data) || !isObject(data) || data._isVNode)) {
     children = data
     data = null
   }