perf: skip initializer extraction for options objects

This commit is contained in:
Evan You 2018-10-26 12:11:52 -04:00
parent 8db26a504c
commit f142c322e0
4 changed files with 28 additions and 15 deletions

View File

@ -15,13 +15,17 @@ export interface ComponentClassOptions<P = {}, This = ComponentInstance> {
computed?: ComponentComputedOptions<This> computed?: ComponentComputedOptions<This>
watch?: ComponentWatchOptions<This> watch?: ComponentWatchOptions<This>
displayName?: string displayName?: string
fromOptions?: boolean
} }
export interface ComponentOptions< export interface ComponentOptions<
P = {}, P = {},
D = {}, D = {},
This = ComponentInstance<P, D> This = ComponentInstance<P, D>
> extends ComponentClassOptions<P, This>, APIMethods<P, D>, LifecycleMethods { >
extends ComponentClassOptions<P, This>,
Partial<APIMethods<P, D>>,
Partial<LifecycleMethods> {
// TODO other options // TODO other options
readonly [key: string]: any readonly [key: string]: any
} }
@ -161,10 +165,7 @@ export function mergeComponentOptions(to: any, from: any): ComponentOptions {
if (isFunction(value) && isFunction(existing)) { if (isFunction(value) && isFunction(existing)) {
if (key === 'data') { if (key === 'data') {
// for data we need to merge the returned value // for data we need to merge the returned value
// TODO: backwards compat requires recursive merge res[key] = mergeDataFn(existing, value)
res[key] = function() {
return Object.assign(existing.call(this), value.call(this))
}
} else if (/^render|^errorCaptured/.test(key)) { } else if (/^render|^errorCaptured/.test(key)) {
// render, renderTracked, renderTriggered & errorCaptured // render, renderTracked, renderTriggered & errorCaptured
// are never merged // are never merged
@ -186,3 +187,11 @@ export function mergeComponentOptions(to: any, from: any): ComponentOptions {
} }
return res return res
} }
export function mergeDataFn(a: Function, b: Function): Function {
// TODO: backwards compat requires recursive merge,
// but maybe we should just warn if we detect clashing keys
return function() {
return Object.assign(a.call(this), b.call(this))
}
}

View File

@ -2,10 +2,15 @@ import { ComponentInstance } from './component'
import { observable } from '@vue/observer' import { observable } from '@vue/observer'
import { isReservedKey } from '@vue/shared' import { isReservedKey } from '@vue/shared'
export function initializeState(instance: ComponentInstance) { export function initializeState(
instance: ComponentInstance,
shouldExtractInitializers: boolean
) {
const { data } = instance.$options const { data } = instance.$options
const rawData = (instance._rawData = (data ? data.call(instance) : {}) as any) const rawData = (instance._rawData = (data ? data.call(instance) : {}) as any)
extractInitializers(instance, rawData) if (shouldExtractInitializers) {
extractInitializers(instance, rawData)
}
instance.$data = observable(rawData || {}) instance.$data = observable(rawData || {})
} }

View File

@ -45,7 +45,7 @@ export function createComponentInstance<T extends Component>(
$proxy, $proxy,
$options: { created, computed, watch } $options: { created, computed, watch }
} = instance } = instance
initializeState(instance) initializeState(instance, !Component.fromOptions)
initializeComputed(instance, computed) initializeComputed(instance, computed)
initializeWatch(instance, watch) initializeWatch(instance, watch)
instance.$slots = currentVNode.slots || EMPTY_OBJ instance.$slots = currentVNode.slots || EMPTY_OBJ
@ -104,7 +104,7 @@ export function initializeComponentInstance(instance: ComponentInstance) {
export function renderInstanceRoot(instance: ComponentInstance): VNode { export function renderInstanceRoot(instance: ComponentInstance): VNode {
let vnode let vnode
try { try {
vnode = instance.$options.render.call( vnode = instance.render.call(
instance.$proxy, instance.$proxy,
instance.$props, instance.$props,
instance.$slots, instance.$slots,
@ -209,6 +209,8 @@ export function createComponentClassFromOptions(
): ComponentClass { ): ComponentClass {
class AnonymousComponent extends Component { class AnonymousComponent extends Component {
static options = options static options = options
// indicate this component was created from options
static fromOptions = true
} }
const proto = AnonymousComponent.prototype as any const proto = AnonymousComponent.prototype as any
for (const key in options) { for (const key in options) {

View File

@ -3,7 +3,8 @@ import { createComponentClassFromOptions } from '../componentUtils'
import { import {
ComponentOptions, ComponentOptions,
resolveComponentOptionsFromClass, resolveComponentOptionsFromClass,
mergeComponentOptions mergeComponentOptions,
mergeDataFn
} from '../componentOptions' } from '../componentOptions'
import { normalizePropsOptions } from '../componentProps' import { normalizePropsOptions } from '../componentProps'
import { extractInitializers } from '../componentState' import { extractInitializers } from '../componentState'
@ -47,11 +48,7 @@ export function mixins(...args: any[]): any {
return extractInitializers(new Class(this.$props)) return extractInitializers(new Class(this.$props))
} }
const { data } = mixin const { data } = mixin
mixin.data = data mixin.data = data ? mergeDataFn(data, extractData) : extractData
? function() {
return Object.assign(data.call(this), extractData.call(this))
}
: extractData
} else { } else {
mixin.props = normalizePropsOptions(mixin.props) mixin.props = normalizePropsOptions(mixin.props)
} }