Skip to content

Commit

Permalink
perf: more light-weight computed (#452)
Browse files Browse the repository at this point in the history
* feat: add customRef

* perf: more light-weight computed
  • Loading branch information
shaonialife committed Aug 6, 2020
1 parent b4e4655 commit 95d87f1
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 19 deletions.
75 changes: 56 additions & 19 deletions src/apis/computed.ts
Original file line number Diff line number Diff line change
@@ -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<T> {
get: () => T
Expand Down Expand Up @@ -31,28 +35,61 @@ export function computed<T>(
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<T>(
{
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
)
Expand Down
33 changes: 33 additions & 0 deletions src/utils/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

0 comments on commit 95d87f1

Please sign in to comment.