Evan You 7 лет назад
Родитель
Сommit
2f1f6b4355

+ 6 - 3
packages/observer/src/effect.ts

@@ -70,10 +70,13 @@ function run(effect: ReactiveEffect, fn: Function, args: any[]): any {
 }
 }
 
 
 export function cleanup(effect: ReactiveEffect) {
 export function cleanup(effect: ReactiveEffect) {
-  for (let i = 0; i < effect.deps.length; i++) {
-    effect.deps[i].delete(effect)
+  const { deps } = effect
+  if (deps.length) {
+    for (let i = 0; i < deps.length; i++) {
+      deps[i].delete(effect)
+    }
+    deps.length = 0
   }
   }
-  effect.deps.length = 0
 }
 }
 
 
 export function track(
 export function track(

+ 9 - 7
packages/runtime-core/src/component.ts

@@ -5,11 +5,12 @@ import {
   observable,
   observable,
   immutable
   immutable
 } from '@vue/observer'
 } from '@vue/observer'
-import { isFunction, EMPTY_OBJ } from '@vue/shared'
+import { EMPTY_OBJ } from '@vue/shared'
 import { RenderProxyHandlers } from './componentProxy'
 import { RenderProxyHandlers } from './componentProxy'
 import { ComponentPropsOptions, ExtractPropTypes } from './componentProps'
 import { ComponentPropsOptions, ExtractPropTypes } from './componentProps'
 import { PROPS, DYNAMIC_SLOTS, FULL_PROPS } from './patchFlags'
 import { PROPS, DYNAMIC_SLOTS, FULL_PROPS } from './patchFlags'
 import { Slots } from './componentSlots'
 import { Slots } from './componentSlots'
+import { STATEFUL_COMPONENT } from './shapeFlags'
 
 
 export type Data = { [key: string]: any }
 export type Data = { [key: string]: any }
 
 
@@ -148,16 +149,17 @@ export function setupStatefulComponent(instance: ComponentInstance) {
 }
 }
 
 
 export function renderComponentRoot(instance: ComponentInstance): VNode {
 export function renderComponentRoot(instance: ComponentInstance): VNode {
-  const { type: Component, renderProxy } = instance
-  if (isFunction(Component)) {
-    return normalizeVNode(Component(instance))
-  } else {
-    if (__DEV__ && !Component.render) {
+  const { type: Component, vnode } = instance
+  if (vnode.shapeFlag & STATEFUL_COMPONENT) {
+    if (__DEV__ && !(Component as any).render) {
       // TODO warn missing render
       // TODO warn missing render
     }
     }
     return normalizeVNode(
     return normalizeVNode(
-      (Component.render as Function).call(renderProxy, instance)
+      (Component as any).render.call(instance.renderProxy, instance)
     )
     )
+  } else {
+    // functional
+    return normalizeVNode((Component as FunctionalComponent)(instance))
   }
   }
 }
 }
 
 

+ 13 - 15
packages/runtime-core/src/createRenderer.ts

@@ -14,14 +14,7 @@ import {
   createComponentInstance,
   createComponentInstance,
   setupStatefulComponent
   setupStatefulComponent
 } from './component'
 } from './component'
-import {
-  isString,
-  isArray,
-  isFunction,
-  isObject,
-  EMPTY_OBJ,
-  EMPTY_ARR
-} from '@vue/shared'
+import { isString, isArray, EMPTY_OBJ, EMPTY_ARR } from '@vue/shared'
 import {
 import {
   TEXT,
   TEXT,
   CLASS,
   CLASS,
@@ -35,6 +28,7 @@ import { queueJob, queuePostFlushCb, flushPostFlushCbs } from './scheduler'
 import { effect, stop, ReactiveEffectOptions } from '@vue/observer'
 import { effect, stop, ReactiveEffectOptions } from '@vue/observer'
 import { resolveProps } from './componentProps'
 import { resolveProps } from './componentProps'
 import { resolveSlots } from './componentSlots'
 import { resolveSlots } from './componentSlots'
+import { ELEMENT, STATEFUL_COMPONENT, FUNCTIONAL_COMPONENT } from './shapeFlags'
 
 
 const prodEffectOptions = {
 const prodEffectOptions = {
   scheduler: queueJob
   scheduler: queueJob
@@ -108,7 +102,7 @@ export function createRenderer(options: RendererOptions) {
     n2: VNode,
     n2: VNode,
     container: HostNode,
     container: HostNode,
     anchor?: HostNode,
     anchor?: HostNode,
-    optimized?: boolean
+    optimized: boolean = false
   ) {
   ) {
     // patching & not same type, unmount old tree
     // patching & not same type, unmount old tree
     if (n1 != null && !isSameType(n1, n2)) {
     if (n1 != null && !isSameType(n1, n2)) {
@@ -117,7 +111,7 @@ export function createRenderer(options: RendererOptions) {
       n1 = null
       n1 = null
     }
     }
 
 
-    const { type } = n2
+    const { type, shapeFlag } = n2
     switch (type) {
     switch (type) {
       case Text:
       case Text:
         processText(n1, n2, container, anchor)
         processText(n1, n2, container, anchor)
@@ -132,10 +126,14 @@ export function createRenderer(options: RendererOptions) {
         processPortal(n1, n2, container, anchor, optimized)
         processPortal(n1, n2, container, anchor, optimized)
         break
         break
       default:
       default:
-        if (isString(type)) {
+        if (shapeFlag & ELEMENT) {
           processElement(n1, n2, container, anchor, optimized)
           processElement(n1, n2, container, anchor, optimized)
         } else {
         } else {
-          if (__DEV__ && !isFunction(type) && !isObject(type)) {
+          if (
+            __DEV__ &&
+            !(shapeFlag & STATEFUL_COMPONENT) &&
+            !(shapeFlag & FUNCTIONAL_COMPONENT)
+          ) {
             // TODO warn invalid node type
             // TODO warn invalid node type
             debugger
             debugger
           }
           }
@@ -453,14 +451,14 @@ export function createRenderer(options: RendererOptions) {
     const instance: ComponentInstance = (vnode.component = createComponentInstance(
     const instance: ComponentInstance = (vnode.component = createComponentInstance(
       Component
       Component
     ))
     ))
-    instance.update = effect(() => {
-      if (!instance.vnode) {
+    instance.update = effect(function updateComponent() {
+      if (instance.vnode === null) {
         // initial mount
         // initial mount
         instance.vnode = vnode
         instance.vnode = vnode
         resolveProps(instance, vnode.props, Component.props)
         resolveProps(instance, vnode.props, Component.props)
         resolveSlots(instance, vnode.children)
         resolveSlots(instance, vnode.children)
         // setup stateful
         // setup stateful
-        if (typeof Component === 'object') {
+        if (vnode.shapeFlag & STATEFUL_COMPONENT) {
           setupStatefulComponent(instance)
           setupStatefulComponent(instance)
         }
         }
         const subTree = (instance.subTree = renderComponentRoot(instance))
         const subTree = (instance.subTree = renderComponentRoot(instance))

+ 3 - 0
packages/runtime-core/src/shapeFlags.ts

@@ -0,0 +1,3 @@
+export const ELEMENT = 1
+export const FUNCTIONAL_COMPONENT = 1 << 1
+export const STATEFUL_COMPONENT = 1 << 2

+ 18 - 1
packages/runtime-core/src/vnode.ts

@@ -3,6 +3,7 @@ import { ComponentInstance } from './component'
 import { HostNode } from './createRenderer'
 import { HostNode } from './createRenderer'
 import { RawSlots } from './componentSlots'
 import { RawSlots } from './componentSlots'
 import { CLASS } from './patchFlags'
 import { CLASS } from './patchFlags'
+import { ELEMENT, FUNCTIONAL_COMPONENT, STATEFUL_COMPONENT } from './shapeFlags'
 
 
 export const Fragment = Symbol('Fragment')
 export const Fragment = Symbol('Fragment')
 export const Text = Symbol('Text')
 export const Text = Symbol('Text')
@@ -36,6 +37,7 @@ export interface VNode {
   target: HostNode | null // portal target
   target: HostNode | null // portal target
 
 
   // optimization only
   // optimization only
+  shapeFlag: number
   patchFlag: number
   patchFlag: number
   dynamicProps: string[] | null
   dynamicProps: string[] | null
   dynamicChildren: VNode[] | null
   dynamicChildren: VNode[] | null
@@ -93,6 +95,15 @@ export function createVNode(
 ): VNode {
 ): VNode {
   // Allow passing 0 for props, this can save bytes on generated code.
   // Allow passing 0 for props, this can save bytes on generated code.
   props = props || null
   props = props || null
+
+  const shapeFlag = isString(type)
+    ? ELEMENT
+    : isFunction(type)
+      ? FUNCTIONAL_COMPONENT
+      : isObject(type)
+        ? STATEFUL_COMPONENT
+        : 0
+
   const vnode: VNode = {
   const vnode: VNode = {
     type,
     type,
     props,
     props,
@@ -102,6 +113,7 @@ export function createVNode(
     el: null,
     el: null,
     anchor: null,
     anchor: null,
     target: null,
     target: null,
+    shapeFlag,
     patchFlag,
     patchFlag,
     dynamicProps,
     dynamicProps,
     dynamicChildren: null
     dynamicChildren: null
@@ -123,7 +135,12 @@ export function createVNode(
   // component nodes also should always be tracked, because even if the
   // component nodes also should always be tracked, because even if the
   // component doesn't need to update, it needs to persist the instance on to
   // component doesn't need to update, it needs to persist the instance on to
   // the next vnode so that it can be properly unmounted later.
   // the next vnode so that it can be properly unmounted later.
-  if (shouldTrack && (patchFlag || isObject(type) || isFunction(type))) {
+  if (
+    shouldTrack &&
+    (patchFlag ||
+      shapeFlag & STATEFUL_COMPONENT ||
+      shapeFlag & FUNCTIONAL_COMPONENT)
+  ) {
     trackDynamicNode(vnode)
     trackDynamicNode(vnode)
   }
   }