refactor: proper options inheritance
This commit is contained in:
parent
2a4e158c76
commit
b83cf4ea38
@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
Component,
|
||||||
ComponentInstance,
|
ComponentInstance,
|
||||||
ComponentClass,
|
ComponentClass,
|
||||||
APIMethods,
|
APIMethods,
|
||||||
@ -104,22 +105,22 @@ export const reservedMethods: ReservedKeys = {
|
|||||||
// This is called in the base component constructor and the return value is
|
// This is called in the base component constructor and the return value is
|
||||||
// set on the instance as $options.
|
// set on the instance as $options.
|
||||||
export function resolveComponentOptionsFromClass(
|
export function resolveComponentOptionsFromClass(
|
||||||
Component: ComponentClass
|
Class: ComponentClass
|
||||||
): ComponentOptions {
|
): ComponentOptions {
|
||||||
if (Component.options) {
|
if (Class.hasOwnProperty('options')) {
|
||||||
return Component.options
|
return Class.options as ComponentOptions
|
||||||
}
|
}
|
||||||
const staticDescriptors = Object.getOwnPropertyDescriptors(Component)
|
let options = {} as any
|
||||||
const options = {} as any
|
|
||||||
|
const staticDescriptors = Object.getOwnPropertyDescriptors(Class)
|
||||||
for (const key in staticDescriptors) {
|
for (const key in staticDescriptors) {
|
||||||
const { enumerable, get, value } = staticDescriptors[key]
|
const { enumerable, get, value } = staticDescriptors[key]
|
||||||
if (enumerable || get) {
|
if (enumerable || get) {
|
||||||
options[key] = get ? get() : value
|
options[key] = get ? get() : value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const instanceDescriptors = Object.getOwnPropertyDescriptors(
|
|
||||||
Component.prototype
|
const instanceDescriptors = Object.getOwnPropertyDescriptors(Class.prototype)
|
||||||
)
|
|
||||||
for (const key in instanceDescriptors) {
|
for (const key in instanceDescriptors) {
|
||||||
const { get, value } = instanceDescriptors[key]
|
const { get, value } = instanceDescriptors[key]
|
||||||
if (get) {
|
if (get) {
|
||||||
@ -127,7 +128,7 @@ export function resolveComponentOptionsFromClass(
|
|||||||
;(options.computed || (options.computed = {}))[key] = get
|
;(options.computed || (options.computed = {}))[key] = get
|
||||||
// there's no need to do anything for the setter
|
// there's no need to do anything for the setter
|
||||||
// as it's already defined on the prototype
|
// as it's already defined on the prototype
|
||||||
} else if (isFunction(value)) {
|
} else if (isFunction(value) && key !== 'constructor') {
|
||||||
if (key in reservedMethods) {
|
if (key in reservedMethods) {
|
||||||
options[key] = value
|
options[key] = value
|
||||||
} else {
|
} else {
|
||||||
@ -135,8 +136,16 @@ export function resolveComponentOptionsFromClass(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
options.props = normalizePropsOptions(options.props)
|
options.props = normalizePropsOptions(options.props)
|
||||||
Component.options = options
|
|
||||||
|
const ParentClass = Object.getPrototypeOf(Class)
|
||||||
|
if (ParentClass !== Component) {
|
||||||
|
const parentOptions = resolveComponentOptionsFromClass(ParentClass)
|
||||||
|
options = mergeComponentOptions(parentOptions, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
Class.options = options
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,7 +163,7 @@ export function mergeComponentOptions(to: any, from: any): ComponentOptions {
|
|||||||
if (key === 'data') {
|
if (key === 'data') {
|
||||||
// for data we need to merge the returned value
|
// for data we need to merge the returned value
|
||||||
res[key] = function() {
|
res[key] = function() {
|
||||||
return Object.assign(existing(), value())
|
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
|
||||||
|
@ -4,17 +4,14 @@ import { observable } from '@vue/observer'
|
|||||||
const internalRE = /^_|^\$/
|
const internalRE = /^_|^\$/
|
||||||
|
|
||||||
export function initializeState(instance: ComponentInstance) {
|
export function initializeState(instance: ComponentInstance) {
|
||||||
if (instance.data) {
|
const { data } = instance.$options
|
||||||
instance._rawData = instance.data()
|
const rawData = (instance._rawData = (data ? data.call(instance) : {}) as any)
|
||||||
} else {
|
|
||||||
const keys = Object.keys(instance)
|
const keys = Object.keys(instance)
|
||||||
const data = (instance._rawData = {} as any)
|
|
||||||
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)) {
|
||||||
data[key] = (instance as any)[key]
|
rawData[key] = (instance as any)[key]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
instance.$data = observable(rawData || {})
|
||||||
instance.$data = observable(instance._rawData || {})
|
|
||||||
}
|
}
|
||||||
|
@ -38,15 +38,22 @@ export function createComponentInstance(
|
|||||||
currentVNode = vnode
|
currentVNode = vnode
|
||||||
currentContextVNode = vnode.contextVNode
|
currentContextVNode = vnode.contextVNode
|
||||||
const instance = (vnode.children = new Component() as ComponentInstance)
|
const instance = (vnode.children = new Component() as ComponentInstance)
|
||||||
|
|
||||||
// then we finish the initialization by collecting properties set on the
|
// then we finish the initialization by collecting properties set on the
|
||||||
// instance
|
// instance
|
||||||
|
const {
|
||||||
|
$proxy,
|
||||||
|
$options: { created, computed, watch }
|
||||||
|
} = instance
|
||||||
initializeState(instance)
|
initializeState(instance)
|
||||||
initializeComputed(instance, instance.$options.computed)
|
initializeComputed(instance, computed)
|
||||||
initializeWatch(instance, instance.$options.watch)
|
initializeWatch(instance, watch)
|
||||||
instance.$slots = currentVNode.slots || EMPTY_OBJ
|
instance.$slots = currentVNode.slots || EMPTY_OBJ
|
||||||
if (instance.created) {
|
|
||||||
instance.created.call(instance.$proxy)
|
if (created) {
|
||||||
|
created.call($proxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
currentVNode = currentContextVNode = null
|
currentVNode = currentContextVNode = null
|
||||||
return instance
|
return instance
|
||||||
}
|
}
|
||||||
@ -87,14 +94,11 @@ export function initializeComponentInstance(instance: ComponentInstance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// beforeCreate hook is called right in the constructor
|
// beforeCreate hook is called right in the constructor
|
||||||
if (instance.beforeCreate) {
|
const { beforeCreate, props } = instance.$options
|
||||||
instance.beforeCreate.call(proxy)
|
if (beforeCreate) {
|
||||||
|
beforeCreate.call(proxy)
|
||||||
}
|
}
|
||||||
initializeProps(
|
initializeProps(instance, props, (currentVNode as VNode).data)
|
||||||
instance,
|
|
||||||
instance.$options.props,
|
|
||||||
(currentVNode as VNode).data
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderInstanceRoot(instance: ComponentInstance): VNode {
|
export function renderInstanceRoot(instance: ComponentInstance): VNode {
|
||||||
|
@ -1167,8 +1167,13 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
;(instance as any).$unmount = unmountComponentInstance
|
;(instance as any).$unmount = unmountComponentInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instance.beforeMount) {
|
const {
|
||||||
instance.beforeMount.call(instance.$proxy)
|
$proxy,
|
||||||
|
$options: { beforeMount, mounted, renderTracked, renderTriggered }
|
||||||
|
} = instance
|
||||||
|
|
||||||
|
if (beforeMount) {
|
||||||
|
beforeMount.call($proxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
const queueUpdate = (instance.$forceUpdate = () => {
|
const queueUpdate = (instance.$forceUpdate = () => {
|
||||||
@ -1200,33 +1205,26 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
instance._mounted = true
|
instance._mounted = true
|
||||||
mountComponentInstanceCallbacks(instance, vnode.ref)
|
if (vnode.ref) {
|
||||||
|
mountRef(vnode.ref, $proxy)
|
||||||
|
}
|
||||||
|
if (mounted) {
|
||||||
|
lifecycleHooks.push(() => {
|
||||||
|
mounted.call($proxy)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
scheduler: queueUpdate,
|
scheduler: queueUpdate,
|
||||||
onTrack: instance.renderTracked,
|
onTrack: renderTracked,
|
||||||
onTrigger: instance.renderTriggered
|
onTrigger: renderTriggered
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return vnode.el as RenderNode
|
return vnode.el as RenderNode
|
||||||
}
|
}
|
||||||
|
|
||||||
function mountComponentInstanceCallbacks(
|
|
||||||
instance: ComponentInstance,
|
|
||||||
ref: Ref | null
|
|
||||||
) {
|
|
||||||
if (ref) {
|
|
||||||
mountRef(ref, instance.$proxy)
|
|
||||||
}
|
|
||||||
if (instance.mounted) {
|
|
||||||
lifecycleHooks.push(() => {
|
|
||||||
;(instance as any).mounted.call(instance.$proxy)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateComponentInstance(
|
function updateComponentInstance(
|
||||||
instance: ComponentInstance,
|
instance: ComponentInstance,
|
||||||
isSVG: boolean
|
isSVG: boolean
|
||||||
@ -1234,23 +1232,22 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
if (__DEV__ && instance.$parentVNode) {
|
if (__DEV__ && instance.$parentVNode) {
|
||||||
pushWarningContext(instance.$parentVNode as VNode)
|
pushWarningContext(instance.$parentVNode as VNode)
|
||||||
}
|
}
|
||||||
const prevVNode = instance.$vnode
|
|
||||||
|
|
||||||
if (instance.beforeUpdate) {
|
const {
|
||||||
instance.beforeUpdate.call(instance.$proxy, prevVNode)
|
$vnode: prevVNode,
|
||||||
|
$parentVNode,
|
||||||
|
$proxy,
|
||||||
|
$options: { beforeUpdate, updated }
|
||||||
|
} = instance
|
||||||
|
if (beforeUpdate) {
|
||||||
|
beforeUpdate.call($proxy, prevVNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextVNode = (instance.$vnode = renderInstanceRoot(
|
const nextVNode = (instance.$vnode = renderInstanceRoot(
|
||||||
instance
|
instance
|
||||||
) as MountedVNode)
|
) as MountedVNode)
|
||||||
const container = platformParentNode(prevVNode.el) as RenderNode
|
const container = platformParentNode(prevVNode.el) as RenderNode
|
||||||
patch(
|
patch(prevVNode, nextVNode, container, $parentVNode as MountedVNode, isSVG)
|
||||||
prevVNode,
|
|
||||||
nextVNode,
|
|
||||||
container,
|
|
||||||
instance.$parentVNode as MountedVNode,
|
|
||||||
isSVG
|
|
||||||
)
|
|
||||||
const el = nextVNode.el as RenderNode
|
const el = nextVNode.el as RenderNode
|
||||||
|
|
||||||
if (__COMPAT__) {
|
if (__COMPAT__) {
|
||||||
@ -1260,7 +1257,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
|
|
||||||
// recursively update contextVNode el for nested HOCs
|
// recursively update contextVNode el for nested HOCs
|
||||||
if ((nextVNode.flags & VNodeFlags.PORTAL) === 0) {
|
if ((nextVNode.flags & VNodeFlags.PORTAL) === 0) {
|
||||||
let vnode = instance.$parentVNode
|
let vnode = $parentVNode
|
||||||
while (vnode !== null) {
|
while (vnode !== null) {
|
||||||
if ((vnode.flags & VNodeFlags.COMPONENT) > 0) {
|
if ((vnode.flags & VNodeFlags.COMPONENT) > 0) {
|
||||||
vnode.el = el
|
vnode.el = el
|
||||||
@ -1269,14 +1266,14 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instance.updated) {
|
if (updated) {
|
||||||
// Because the child's update is executed by the scheduler and not
|
// Because the child's update is executed by the scheduler and not
|
||||||
// synchronously within the parent's update call, the child's updated hook
|
// synchronously within the parent's update call, the child's updated hook
|
||||||
// will be added to the queue AFTER the parent's, but they should be
|
// will be added to the queue AFTER the parent's, but they should be
|
||||||
// invoked BEFORE the parent's. Therefore we add them to the head of the
|
// invoked BEFORE the parent's. Therefore we add them to the head of the
|
||||||
// queue instead.
|
// queue instead.
|
||||||
lifecycleHooks.unshift(() => {
|
lifecycleHooks.unshift(() => {
|
||||||
;(instance as any).updated.call(instance.$proxy, nextVNode)
|
updated.call($proxy, nextVNode)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1299,17 +1296,23 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
if (instance._unmounted) {
|
if (instance._unmounted) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (instance.beforeUnmount) {
|
const {
|
||||||
instance.beforeUnmount.call(instance.$proxy)
|
$vnode,
|
||||||
|
$proxy,
|
||||||
|
_updateHandle,
|
||||||
|
$options: { beforeUnmount, unmounted }
|
||||||
|
} = instance
|
||||||
|
if (beforeUnmount) {
|
||||||
|
beforeUnmount.call($proxy)
|
||||||
}
|
}
|
||||||
if (instance.$vnode) {
|
if ($vnode) {
|
||||||
unmount(instance.$vnode)
|
unmount($vnode)
|
||||||
}
|
}
|
||||||
stop(instance._updateHandle)
|
stop(_updateHandle)
|
||||||
teardownComponentInstance(instance)
|
teardownComponentInstance(instance)
|
||||||
instance._unmounted = true
|
instance._unmounted = true
|
||||||
if (instance.unmounted) {
|
if (unmounted) {
|
||||||
instance.unmounted.call(instance.$proxy)
|
unmounted.call($proxy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1338,12 +1341,16 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
}
|
}
|
||||||
if (asRoot || !instance._inactiveRoot) {
|
if (asRoot || !instance._inactiveRoot) {
|
||||||
// 2. recursively call activated on child tree, depth-first
|
// 2. recursively call activated on child tree, depth-first
|
||||||
const { $children } = instance
|
const {
|
||||||
|
$children,
|
||||||
|
$proxy,
|
||||||
|
$options: { activated }
|
||||||
|
} = instance
|
||||||
for (let i = 0; i < $children.length; i++) {
|
for (let i = 0; i < $children.length; i++) {
|
||||||
callActivatedHook($children[i], false)
|
callActivatedHook($children[i], false)
|
||||||
}
|
}
|
||||||
if (instance.activated) {
|
if (activated) {
|
||||||
instance.activated.call(instance.$proxy)
|
activated.call($proxy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1359,12 +1366,16 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
}
|
}
|
||||||
if (asRoot || !instance._inactiveRoot) {
|
if (asRoot || !instance._inactiveRoot) {
|
||||||
// 2. recursively call deactivated on child tree, depth-first
|
// 2. recursively call deactivated on child tree, depth-first
|
||||||
const { $children } = instance
|
const {
|
||||||
|
$children,
|
||||||
|
$proxy,
|
||||||
|
$options: { deactivated }
|
||||||
|
} = instance
|
||||||
for (let i = 0; i < $children.length; i++) {
|
for (let i = 0; i < $children.length; i++) {
|
||||||
callDeactivateHook($children[i], false)
|
callDeactivateHook($children[i], false)
|
||||||
}
|
}
|
||||||
if (instance.deactivated) {
|
if (deactivated) {
|
||||||
instance.deactivated.call(instance.$proxy)
|
deactivated.call($proxy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,7 @@ interface createElement extends VNodeFactories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const h = ((tag: ElementType, data?: any, children?: any): VNode => {
|
export const h = ((tag: ElementType, data?: any, children?: any): VNode => {
|
||||||
if (isArray(data) || !isObject(data) || data._isVNode) {
|
if (data !== null && (isArray(data) || !isObject(data) || data._isVNode)) {
|
||||||
children = data
|
children = data
|
||||||
data = null
|
data = null
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user