diff --git a/src/apis/computed.ts b/src/apis/computed.ts index a3753e70..cdeaafbc 100644 --- a/src/apis/computed.ts +++ b/src/apis/computed.ts @@ -1,7 +1,11 @@ import { getVueConstructor, getCurrentInstance } from '../runtimeContext' import { createRef, Ref } from '../reactivity' -import { defineComponentInstance } from '../utils/helper' -import { warn } from '../utils' +import { + warn, + noopFn, + defineComponentInstance, + getVueInternalClasses, +} from '../utils' interface Option { get: () => T @@ -31,28 +35,61 @@ export function computed( set = options.set } - const computedHost = defineComponentInstance(getVueConstructor(), { - computed: { - $$state: { - get, - set, + let computedSetter + let computedGetter + + if (vm) { + const { Watcher, Dep } = getVueInternalClasses() + let watcher: any + computedGetter = () => { + if (!watcher) { + watcher = new Watcher(vm, get, noopFn, { lazy: true }) + } + if (watcher.dirty) { + watcher.evaluate() + } + if (Dep.target) { + watcher.depend() + } + return watcher.value + } + + computedSetter = (v: T) => { + if (__DEV__ && !set) { + warn('Write operation failed: computed value is readonly.', vm!) + return + } + + if (set) { + set(v) + } + } + } else { + // fallback + const computedHost = defineComponentInstance(getVueConstructor(), { + computed: { + $$state: { + get, + set, + }, }, - }, - }) + }) + + computedGetter = () => (computedHost as any).$$state + computedSetter = (v: T) => { + if (__DEV__ && !set) { + warn('Write operation failed: computed value is readonly.', vm!) + return + } - vm && vm.$on('hook:destroyed', () => computedHost.$destroy()) + ;(computedHost as any).$$state = v + } + } return createRef( { - get: () => (computedHost as any).$$state, - set: (v: T) => { - if (__DEV__ && !set) { - warn('Write operation failed: computed value is readonly.', vm!) - return - } - - ;(computedHost as any).$$state = v - }, + get: computedGetter, + set: computedSetter, }, !set ) diff --git a/src/utils/helper.ts b/src/utils/helper.ts index c3dfd531..c79f31e3 100644 --- a/src/utils/helper.ts +++ b/src/utils/helper.ts @@ -72,3 +72,36 @@ export function resolveSlots( return res } + +let vueInternalClasses: + | { + Watcher: any + Dep: any + } + | undefined + +export const getVueInternalClasses = () => { + if (!vueInternalClasses) { + const vm: any = defineComponentInstance(getVueConstructor(), { + computed: { + value() { + return 0 + }, + }, + }) + + // to get Watcher class + const Watcher = vm._computedWatchers.value.constructor + // to get Dep class + const Dep = vm._data.__ob__.dep.constructor + + vueInternalClasses = { + Watcher, + Dep, + } + + vm.$destroy() + } + + return vueInternalClasses +}