feat: support initializers in mixins

This commit is contained in:
Evan You 2018-10-16 20:04:58 -04:00
parent 456ee13ec9
commit 7ce16ea8d6
3 changed files with 38 additions and 9 deletions

View File

@ -137,8 +137,16 @@ class InternalComponent implements PublicInstanceMethods {
_isVue: boolean = true _isVue: boolean = true
_inactiveRoot: boolean = false _inactiveRoot: boolean = false
constructor() { constructor(props?: object) {
initializeComponentInstance(this as any) if (props === void 0) {
initializeComponentInstance(this as any)
} else {
// the presence of the props argument indicates that this class is being
// instantiated as a mixin, and should expose the props on itself
// so that the extended class constructor (and property initializers) can
// access $props.
this.$props = props
}
} }
// to be set by renderer during mount // to be set by renderer during mount

View File

@ -6,12 +6,21 @@ const internalRE = /^_|^\$/
export function initializeState(instance: ComponentInstance) { export function initializeState(instance: ComponentInstance) {
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)
instance.$data = observable(rawData || {})
}
// extract properties initialized in a component's constructor
export function extractInitializers(
instance: ComponentInstance,
data: any = {}
): any {
const keys = Object.keys(instance) const keys = Object.keys(instance)
for (let i = 0; i < keys.length; i++) { for (let i = 0; i < keys.length; i++) {
const key = keys[i] const key = keys[i]
if (!internalRE.test(key)) { if (!internalRE.test(key)) {
rawData[key] = (instance as any)[key] data[key] = (instance as any)[key]
} }
} }
instance.$data = observable(rawData || {}) return data
} }

View File

@ -6,6 +6,7 @@ import {
mergeComponentOptions mergeComponentOptions
} from '../componentOptions' } from '../componentOptions'
import { normalizePropsOptions } from '../componentProps' import { normalizePropsOptions } from '../componentProps'
import { extractInitializers } from '../componentState'
import { isFunction } from '@vue/shared' import { isFunction } from '@vue/shared'
interface ComponentConstructor<This = Component> { interface ComponentConstructor<This = Component> {
@ -36,14 +37,25 @@ export function mixins(...args: any[]): any {
let options: ComponentOptions = {} let options: ComponentOptions = {}
args.forEach(mixin => { args.forEach(mixin => {
if (isFunction(mixin)) { if (isFunction(mixin)) {
options = mergeComponentOptions( const Class = mixin
options, mixin = resolveComponentOptionsFromClass(Class)
resolveComponentOptionsFromClass(mixin) // in order to extract properties initialized in the mixin's constructor,
) // we create an instance of it and pass in the actual props - this
// short-circuits the normal component initialization and allows us to
// relatively-cheaply extract the properties added in the constructor.
function extractData() {
return extractInitializers(new Class(this.$props))
}
const { data } = mixin
mixin.data = data
? function() {
return Object.assign(data.call(this), extractData.call(this))
}
: extractData
} else { } else {
mixin.props = normalizePropsOptions(mixin.props) mixin.props = normalizePropsOptions(mixin.props)
options = mergeComponentOptions(options, mixin)
} }
options = mergeComponentOptions(options, mixin)
}) })
return createComponentClassFromOptions(options) return createComponentClassFromOptions(options)
} }