mirror of
https://gitee.com/vuejs/vue.git
synced 2024-12-03 12:38:24 +08:00
move computed properties definition to component prototype when possible
This commit is contained in:
parent
4f6b1014b3
commit
406352baba
@ -52,6 +52,7 @@ declare interface Component {
|
||||
_renderContext: ?Component;
|
||||
_watcher: Watcher;
|
||||
_watchers: Array<Watcher>;
|
||||
_computedWatchers: { [key: string]: Watcher };
|
||||
_data: Object;
|
||||
_props: Object;
|
||||
_events: Object;
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
import config from '../config'
|
||||
import { warn, mergeOptions } from '../util/index'
|
||||
import { defineComputed } from '../instance/state'
|
||||
|
||||
export function initExtend (Vue: GlobalAPI) {
|
||||
/**
|
||||
@ -23,6 +24,7 @@ export function initExtend (Vue: GlobalAPI) {
|
||||
if (cachedCtors[SuperId]) {
|
||||
return cachedCtors[SuperId]
|
||||
}
|
||||
|
||||
const name = extendOptions.name || Super.options.name
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (!/^[a-zA-Z][\w-]*$/.test(name)) {
|
||||
@ -33,6 +35,7 @@ export function initExtend (Vue: GlobalAPI) {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const Sub = function VueComponent (options) {
|
||||
this._init(options)
|
||||
}
|
||||
@ -44,10 +47,16 @@ export function initExtend (Vue: GlobalAPI) {
|
||||
extendOptions
|
||||
)
|
||||
Sub['super'] = Super
|
||||
|
||||
if (Sub.options.computed) {
|
||||
initComputed(Sub)
|
||||
}
|
||||
|
||||
// allow further extension/mixin/plugin usage
|
||||
Sub.extend = Super.extend
|
||||
Sub.mixin = Super.mixin
|
||||
Sub.use = Super.use
|
||||
|
||||
// create asset registers, so extended classes
|
||||
// can have their private assets too.
|
||||
config._assetTypes.forEach(function (type) {
|
||||
@ -57,13 +66,22 @@ export function initExtend (Vue: GlobalAPI) {
|
||||
if (name) {
|
||||
Sub.options.components[name] = Sub
|
||||
}
|
||||
|
||||
// keep a reference to the super options at extension time.
|
||||
// later at instantiation we can check if Super's options have
|
||||
// been updated.
|
||||
Sub.superOptions = Super.options
|
||||
Sub.extendOptions = extendOptions
|
||||
|
||||
// cache constructor
|
||||
cachedCtors[SuperId] = Sub
|
||||
return Sub
|
||||
}
|
||||
}
|
||||
|
||||
function initComputed (Comp) {
|
||||
const computed = Comp.options.computed
|
||||
for (const key in computed) {
|
||||
defineComputed(Comp.prototype, key, computed[key])
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* @flow */
|
||||
|
||||
import Watcher from '../observer/watcher'
|
||||
import Dep from '../observer/dep'
|
||||
import Watcher from '../observer/watcher'
|
||||
|
||||
import {
|
||||
set,
|
||||
@ -108,6 +108,26 @@ function initData (vm: Component) {
|
||||
observe(data, true /* asRootData */)
|
||||
}
|
||||
|
||||
const computedWatcherOptions = { lazy: true }
|
||||
|
||||
function initComputed (vm: Component, computed: Object) {
|
||||
const watchers = vm._computedWatchers = Object.create(null)
|
||||
|
||||
for (const key in computed) {
|
||||
const userDef = computed[key]
|
||||
const getter = typeof userDef === 'function' ? userDef : userDef.get
|
||||
// create internal watcher for the computed property.
|
||||
watchers[key] = new Watcher(vm, getter, noop, computedWatcherOptions)
|
||||
|
||||
// component-defined computed properties are already defined on the
|
||||
// component prototype. We only need to define on-the-fly computed
|
||||
// properties here.
|
||||
if (!(key in vm)) {
|
||||
defineComputed(vm, key, userDef)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const computedSharedDefinition = {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
@ -115,46 +135,35 @@ const computedSharedDefinition = {
|
||||
set: noop
|
||||
}
|
||||
|
||||
function initComputed (vm: Component, computed: Object) {
|
||||
for (const key in computed) {
|
||||
/* istanbul ignore if */
|
||||
if (process.env.NODE_ENV !== 'production' && key in vm) {
|
||||
warn(
|
||||
`existing instance property "${key}" will be ` +
|
||||
`overwritten by a computed property with the same name.`,
|
||||
vm
|
||||
)
|
||||
}
|
||||
const userDef = computed[key]
|
||||
if (typeof userDef === 'function') {
|
||||
computedSharedDefinition.get = makeComputedGetter(userDef, vm)
|
||||
computedSharedDefinition.set = noop
|
||||
} else {
|
||||
computedSharedDefinition.get = userDef.get
|
||||
? userDef.cache !== false
|
||||
? makeComputedGetter(userDef.get, vm)
|
||||
: bind(userDef.get, vm)
|
||||
: noop
|
||||
computedSharedDefinition.set = userDef.set
|
||||
? bind(userDef.set, vm)
|
||||
: noop
|
||||
}
|
||||
Object.defineProperty(vm, key, computedSharedDefinition)
|
||||
export function defineComputed (target: any, key: string, userDef: Object | Function) {
|
||||
if (typeof userDef === 'function') {
|
||||
computedSharedDefinition.get = createComputedGetter(key)
|
||||
computedSharedDefinition.set = noop
|
||||
} else {
|
||||
computedSharedDefinition.get = userDef.get
|
||||
? userDef.cache !== false
|
||||
? createComputedGetter(key)
|
||||
: userDef.get
|
||||
: noop
|
||||
computedSharedDefinition.set = userDef.set
|
||||
? userDef.set
|
||||
: noop
|
||||
}
|
||||
Object.defineProperty(target, key, computedSharedDefinition)
|
||||
}
|
||||
|
||||
function makeComputedGetter (getter: Function, owner: Component): Function {
|
||||
const watcher = new Watcher(owner, getter, noop, {
|
||||
lazy: true
|
||||
})
|
||||
function createComputedGetter (key) {
|
||||
return function computedGetter () {
|
||||
if (watcher.dirty) {
|
||||
watcher.evaluate()
|
||||
const watcher = this._computedWatchers && this._computedWatchers[key]
|
||||
if (watcher) {
|
||||
if (watcher.dirty) {
|
||||
watcher.evaluate()
|
||||
}
|
||||
if (Dep.target) {
|
||||
watcher.depend()
|
||||
}
|
||||
return watcher.value
|
||||
}
|
||||
if (Dep.target) {
|
||||
watcher.depend()
|
||||
}
|
||||
return watcher.value
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,4 +107,37 @@ describe('Options computed', () => {
|
||||
vm.b
|
||||
expect(spy.calls.count()).toBe(2)
|
||||
})
|
||||
|
||||
it('as component', done => {
|
||||
const Comp = Vue.extend({
|
||||
template: `<div>{{ b }} {{ c }}</div>`,
|
||||
data () {
|
||||
return { a: 1 }
|
||||
},
|
||||
computed: {
|
||||
// defined on prototype
|
||||
b () {
|
||||
return this.a + 1
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const vm = new Comp({
|
||||
computed: {
|
||||
// defined at instantiation
|
||||
c () {
|
||||
return this.b + 1
|
||||
}
|
||||
}
|
||||
}).$mount()
|
||||
expect(vm.b).toBe(2)
|
||||
expect(vm.c).toBe(3)
|
||||
expect(vm.$el.textContent).toBe('2 3')
|
||||
vm.a = 2
|
||||
expect(vm.b).toBe(3)
|
||||
expect(vm.c).toBe(4)
|
||||
waitForUpdate(() => {
|
||||
expect(vm.$el.textContent).toBe('3 4')
|
||||
}).then(done)
|
||||
})
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user