Browse Source

refactor: use more efficient useComputed() implementation

Evan You 7 years ago
parent
commit
8602b61efb

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

@@ -1,12 +1,15 @@
 import { autorun } from './index'
 import { autorun } from './index'
 import { Autorun, activeAutorunStack } from './autorun'
 import { Autorun, activeAutorunStack } from './autorun'
 
 
-export interface ComputedGetter {
-  (): any
+export interface ComputedGetter<T = any> {
+  (): T
   runner: Autorun
   runner: Autorun
 }
 }
 
 
-export function computed(getter: Function, context?: any): ComputedGetter {
+export function computed<T, C = null>(
+  getter: (this: C, ctx: C) => T,
+  context?: C
+): ComputedGetter<T> {
   let dirty: boolean = true
   let dirty: boolean = true
   let value: any = undefined
   let value: any = undefined
   const runner = autorun(() => getter.call(context, context), {
   const runner = autorun(() => getter.call(context, context), {

+ 3 - 4
packages/runtime-core/src/componentComputed.ts

@@ -3,6 +3,8 @@ import { computed, stop, ComputedGetter } from '@vue/observer'
 import { ComponentInstance } from './component'
 import { ComponentInstance } from './component'
 import { ComponentComputedOptions } from './componentOptions'
 import { ComponentComputedOptions } from './componentOptions'
 
 
+export type ComputedHandles = Record<string, ComputedGetter>
+
 export function initializeComputed(
 export function initializeComputed(
   instance: ComponentInstance,
   instance: ComponentInstance,
   computedOptions: ComponentComputedOptions | undefined
   computedOptions: ComponentComputedOptions | undefined
@@ -10,10 +12,7 @@ export function initializeComputed(
   if (!computedOptions) {
   if (!computedOptions) {
     return
     return
   }
   }
-  const handles: Record<
-    string,
-    ComputedGetter
-  > = (instance._computedGetters = {})
+  const handles: ComputedHandles = (instance._computedGetters = {})
   const proxy = instance.$proxy
   const proxy = instance.$proxy
   for (const key in computedOptions) {
   for (const key in computedOptions) {
     const option = computedOptions[key]
     const option = computedOptions[key]

+ 2 - 1
packages/runtime-core/src/componentProxy.ts

@@ -43,7 +43,8 @@ const renderProxyHandlers = {
     } else if (key[0] !== '_') {
     } else if (key[0] !== '_') {
       if (__DEV__ && isRendering && !(key in target)) {
       if (__DEV__ && isRendering && !(key in target)) {
         warn(
         warn(
-          `property "${key}" was accessed during render but does not exist on instance.`
+          `property "${key}" was accessed during render but does not exist ` +
+            `on instance.`
         )
         )
       }
       }
       const value = Reflect.get(target, key, receiver)
       const value = Reflect.get(target, key, receiver)

+ 7 - 7
packages/runtime-core/src/experimental/hooks.ts

@@ -1,7 +1,7 @@
 import { ComponentInstance, FunctionalComponent, Component } from '../component'
 import { ComponentInstance, FunctionalComponent, Component } from '../component'
 import { mergeLifecycleHooks, Data, WatchOptions } from '../componentOptions'
 import { mergeLifecycleHooks, Data, WatchOptions } from '../componentOptions'
 import { VNode, Slots } from '../vdom'
 import { VNode, Slots } from '../vdom'
-import { observable, computed, stop, ComputedGetter } from '@vue/observer'
+import { observable, computed } from '@vue/observer'
 import { setupWatcher } from '../componentWatch'
 import { setupWatcher } from '../componentWatch'
 
 
 type RawEffect = () => (() => void) | void
 type RawEffect = () => (() => void) | void
@@ -191,14 +191,14 @@ export function useWatch<T>(
 }
 }
 
 
 export function useComputed<T>(getter: () => T): T {
 export function useComputed<T>(getter: () => T): T {
-  const computedRef = useRef()
-  useUnmounted(() => {
-    stop((computedRef.current as ComputedGetter).runner)
-  })
+  ensureCurrentInstance()
+  const id = `__hooksComputed${++callIndex}`
+  const instance = currentInstance as ComponentInstance
+  const handles = instance._computedGetters || (instance._computedGetters = {})
   if (isMounting) {
   if (isMounting) {
-    computedRef.current = computed(getter)
+    handles[id] = computed(getter)
   }
   }
-  return (computedRef.current as ComputedGetter)()
+  return handles[id]()
 }
 }
 
 
 export function withHooks(render: FunctionalComponent): new () => Component {
 export function withHooks(render: FunctionalComponent): new () => Component {