import { ReactiveEffect, DebuggerEvent, ComputedGetter } from '@vue/observer'
import { nextTick } from '@vue/scheduler'
import { ErrorTypes } from './errorHandling'
-import { initializeComponentInstance } from './componentUtils'
+import { initializeComponentInstance } from './componentInstance'
import { EventEmitter, invokeListeners } from './optional/eventEmitter'
import { warn } from './warning'
--- /dev/null
+import { VNode, MountedVNode } from './vdom'
+import { Component, ComponentInstance, ComponentClass } from './component'
+import { initializeState } from './componentState'
+import { initializeProps } from './componentProps'
+import { initializeWatch, teardownWatch } from './componentWatch'
+import { initializeComputed, teardownComputed } from './componentComputed'
+import { createRenderProxy } from './componentProxy'
+import { resolveComponentOptionsFromClass } from './componentOptions'
+import { VNodeFlags } from './flags'
+import { ErrorTypes, callLifecycleHookWithHandler } from './errorHandling'
+import { stop } from '@vue/observer'
+import { EMPTY_OBJ } from '@vue/shared'
+
+let currentVNode: VNode | null = null
+let currentContextVNode: VNode | null = null
+
+export function createComponentInstance<T extends Component>(
+ vnode: VNode
+): ComponentInstance {
+ // component instance creation is done in two steps.
+ // first, `initializeComponentInstance` is called inside base component
+ // constructor as the instance is created so that the extended component's
+ // constructor has access to certain properties and most importantly,
+ // this.$props.
+ // we are storing the vnodes in variables here so that there's no need to
+ // always pass args in super()
+ currentVNode = vnode
+ currentContextVNode = vnode.contextVNode
+ const Component = vnode.tag as ComponentClass
+ const instance = (vnode.children = new Component() as ComponentInstance)
+
+ // then we finish the initialization by collecting properties set on the
+ // instance
+ const {
+ $proxy,
+ $options: { created, computed, watch }
+ } = instance
+ initializeState(instance, !Component.fromOptions)
+ initializeComputed(instance, computed)
+ initializeWatch(instance, watch)
+ instance.$slots = currentVNode.slots || EMPTY_OBJ
+
+ if (created) {
+ callLifecycleHookWithHandler(created, $proxy, ErrorTypes.CREATED)
+ }
+
+ currentVNode = currentContextVNode = null
+ return instance
+}
+
+// this is called inside the base component's constructor
+// it initializes all the way up to props so that they are available
+// inside the extended component's constructor
+export function initializeComponentInstance(instance: ComponentInstance) {
+ if (__DEV__ && currentVNode === null) {
+ throw new Error(
+ `Component classes are not meant to be manually instantiated.`
+ )
+ }
+
+ instance.$options = resolveComponentOptionsFromClass(instance.constructor)
+ instance.$parentVNode = currentVNode as MountedVNode
+
+ // renderProxy
+ const proxy = (instance.$proxy = createRenderProxy(instance))
+
+ // parent chain management
+ if (currentContextVNode !== null) {
+ // locate first non-functional parent
+ while (currentContextVNode !== null) {
+ if ((currentContextVNode.flags & VNodeFlags.COMPONENT_STATEFUL) > 0) {
+ const parentComponent = (currentContextVNode as VNode)
+ .children as ComponentInstance
+ instance.$parent = parentComponent.$proxy
+ instance.$root = parentComponent.$root
+ parentComponent.$children.push(proxy)
+ break
+ }
+ currentContextVNode = currentContextVNode.contextVNode
+ }
+ } else {
+ instance.$root = proxy
+ }
+
+ // beforeCreate hook is called right in the constructor
+ const { beforeCreate, props } = instance.$options
+ if (beforeCreate) {
+ callLifecycleHookWithHandler(beforeCreate, proxy, ErrorTypes.BEFORE_CREATE)
+ }
+ initializeProps(instance, props, (currentVNode as VNode).data)
+}
+
+export function teardownComponentInstance(instance: ComponentInstance) {
+ const parentComponent = instance.$parent && instance.$parent._self
+ if (parentComponent && !parentComponent._unmounted) {
+ parentComponent.$children.splice(
+ parentComponent.$children.indexOf(instance.$proxy),
+ 1
+ )
+ }
+ stop(instance._update)
+ teardownComputed(instance)
+ teardownWatch(instance)
+}
} from './component'
import { isArray, isObject, isFunction } from '@vue/shared'
import { normalizePropsOptions } from './componentProps'
+import { warn } from './warning'
+import { h } from './h'
export type Data = Record<string, any>
return options
}
+export function createComponentClassFromOptions(
+ options: ComponentOptions
+): ComponentClass {
+ class AnonymousComponent extends Component {
+ static options = options
+ // indicate this component was created from options
+ static fromOptions = true
+ }
+ const proto = AnonymousComponent.prototype as any
+ for (const key in options) {
+ const value = options[key]
+ if (key === 'render') {
+ if (__COMPAT__) {
+ options.render = function() {
+ return value.call(this, h)
+ }
+ }
+ // so that we can call instance.render directly
+ proto.render = options.render
+ } else if (key === 'computed') {
+ // create computed setters on prototype
+ // (getters are handled by the render proxy)
+ for (const computedKey in value) {
+ const computed = value[computedKey]
+ const set = isObject(computed) && computed.set
+ if (set) {
+ Object.defineProperty(proto, computedKey, {
+ configurable: true,
+ set
+ })
+ }
+ }
+ } else if (key === 'methods') {
+ for (const method in value) {
+ if (__DEV__ && proto.hasOwnProperty(method)) {
+ warn(
+ `Object syntax contains method name that conflicts with ` +
+ `lifecycle hook: "${method}"`
+ )
+ }
+ proto[method] = value[method]
+ }
+ } else if (__COMPAT__) {
+ if (key === 'name') {
+ options.displayName = value
+ } else if (key === 'render') {
+ options.render = function() {
+ return value.call(this, h)
+ }
+ } else if (key === 'beforeDestroy') {
+ options.beforeUnmount = value
+ } else if (key === 'destroyed') {
+ options.unmounted = value
+ }
+ }
+ }
+ return AnonymousComponent as ComponentClass
+}
+
export function mergeComponentOptions(to: any, from: any): ComponentOptions {
const res: any = Object.assign({}, to)
if (from.mixins) {
import { ComponentInstance } from './component'
import { isFunction, isReservedKey } from '@vue/shared'
import { warn } from './warning'
-import { isRendering } from './componentUtils'
+import { isRendering } from './componentRenderUtils'
import { isObservable } from '@vue/observer'
import { reservedMethods } from './componentOptions'
--- /dev/null
+import { VNode, createFragment, createTextVNode, cloneVNode } from './vdom'
+import { ComponentInstance, FunctionalComponent } from './component'
+import { resolveProps } from './componentProps'
+import { handleError, ErrorTypes } from './errorHandling'
+import { VNodeFlags, ChildrenFlags } from './flags'
+import { EMPTY_OBJ, isArray, isObject } from '@vue/shared'
+import { setCurrentInstance, unsetCurrentInstance } from './experimental/hooks'
+
+export let isRendering = false
+
+export function renderInstanceRoot(instance: ComponentInstance): VNode {
+ let vnode
+ const {
+ $options: { hooks },
+ render,
+ $proxy,
+ $props,
+ $slots,
+ $attrs,
+ $parentVNode
+ } = instance
+ try {
+ setCurrentInstance(instance)
+ if (hooks) {
+ instance._hookProps = hooks.call($proxy, $props) || null
+ }
+ if (__DEV__) {
+ isRendering = true
+ }
+ vnode = render.call($proxy, $props, $slots, $attrs, $parentVNode)
+ if (__DEV__) {
+ isRendering = false
+ }
+ unsetCurrentInstance()
+ } catch (err) {
+ handleError(err, instance, ErrorTypes.RENDER)
+ }
+ return normalizeComponentRoot(vnode, $parentVNode)
+}
+
+export function renderFunctionalRoot(vnode: VNode): VNode {
+ const render = vnode.tag as FunctionalComponent
+ const { 0: props, 1: attrs } = resolveProps(vnode.data, render.props)
+ let subTree
+ try {
+ subTree = render(props, vnode.slots || EMPTY_OBJ, attrs, vnode)
+ } catch (err) {
+ handleError(err, vnode, ErrorTypes.RENDER)
+ }
+ return normalizeComponentRoot(subTree, vnode)
+}
+
+function normalizeComponentRoot(
+ vnode: any,
+ componentVNode: VNode | null
+): VNode {
+ if (vnode == null) {
+ vnode = createTextVNode('')
+ } else if (!isObject(vnode)) {
+ vnode = createTextVNode(vnode + '')
+ } else if (isArray(vnode)) {
+ if (vnode.length === 1) {
+ vnode = normalizeComponentRoot(vnode[0], componentVNode)
+ } else {
+ vnode = createFragment(vnode)
+ }
+ } else {
+ const { el, flags } = vnode
+ if (
+ componentVNode &&
+ (flags & VNodeFlags.COMPONENT || flags & VNodeFlags.ELEMENT)
+ ) {
+ if (el) {
+ vnode = cloneVNode(vnode as VNode)
+ }
+ if (flags & VNodeFlags.COMPONENT) {
+ vnode.parentVNode = componentVNode
+ }
+ } else if (el) {
+ vnode = cloneVNode(vnode as VNode)
+ }
+ }
+ return vnode
+}
+
+export function shouldUpdateComponent(
+ prevVNode: VNode,
+ nextVNode: VNode
+): boolean {
+ const { data: prevProps, childFlags: prevChildFlags } = prevVNode
+ const { data: nextProps, childFlags: nextChildFlags } = nextVNode
+ // If has different slots content, or has non-compiled slots,
+ // the child needs to be force updated.
+ if (
+ prevChildFlags !== nextChildFlags ||
+ (nextChildFlags & ChildrenFlags.DYNAMIC_SLOTS) > 0
+ ) {
+ return true
+ }
+ if (prevProps === nextProps) {
+ return false
+ }
+ if (prevProps === null) {
+ return nextProps !== null
+ }
+ if (nextProps === null) {
+ return prevProps !== null
+ }
+ const nextKeys = Object.keys(nextProps)
+ if (nextKeys.length !== Object.keys(prevProps).length) {
+ return true
+ }
+ for (let i = 0; i < nextKeys.length; i++) {
+ const key = nextKeys[i]
+ if (nextProps[key] !== prevProps[key]) {
+ return true
+ }
+ }
+ return false
+}
+
+// DEV only
+export function getReasonForComponentUpdate(
+ prevVNode: VNode,
+ nextVNode: VNode
+): any {
+ const reasons = []
+ const { childFlags: prevChildFlags } = prevVNode
+ const { childFlags: nextChildFlags } = nextVNode
+ if (
+ prevChildFlags !== nextChildFlags ||
+ (nextChildFlags & ChildrenFlags.DYNAMIC_SLOTS) > 0
+ ) {
+ reasons.push({
+ type: `slots may have changed`,
+ tip: `use function slots + $stable: true to avoid slot-triggered child updates.`
+ })
+ }
+ const prevProps = prevVNode.data || EMPTY_OBJ
+ const nextProps = nextVNode.data || EMPTY_OBJ
+ for (const key in nextProps) {
+ if (nextProps[key] !== prevProps[key]) {
+ reasons.push({
+ type: 'prop changed',
+ key,
+ value: nextProps[key],
+ oldValue: prevProps[key]
+ })
+ }
+ }
+ for (const key in prevProps) {
+ if (!(key in nextProps)) {
+ reasons.push({
+ type: 'prop changed',
+ key,
+ value: undefined,
+ oldValue: prevProps[key]
+ })
+ }
+ }
+ return {
+ type: 'triggered by parent',
+ reasons
+ }
+}
+++ /dev/null
-import { VNodeFlags, ChildrenFlags } from './flags'
-import { EMPTY_OBJ, isArray, isObject } from '@vue/shared'
-import { h } from './h'
-import { VNode, MountedVNode, createFragment } from './vdom'
-import {
- Component,
- ComponentInstance,
- ComponentClass,
- FunctionalComponent
-} from './component'
-import { createTextVNode, cloneVNode } from './vdom'
-import { initializeState } from './componentState'
-import { initializeProps, resolveProps } from './componentProps'
-import { initializeComputed, teardownComputed } from './componentComputed'
-import { initializeWatch, teardownWatch } from './componentWatch'
-import {
- ComponentOptions,
- resolveComponentOptionsFromClass
-} from './componentOptions'
-import { createRenderProxy } from './componentProxy'
-import {
- handleError,
- ErrorTypes,
- callLifecycleHookWithHandler
-} from './errorHandling'
-import { warn } from './warning'
-import { setCurrentInstance, unsetCurrentInstance } from './experimental/hooks'
-import { stop } from '@vue/observer'
-
-let currentVNode: VNode | null = null
-let currentContextVNode: VNode | null = null
-
-export function createComponentInstance<T extends Component>(
- vnode: VNode
-): ComponentInstance {
- // component instance creation is done in two steps.
- // first, `initializeComponentInstance` is called inside base component
- // constructor as the instance is created so that the extended component's
- // constructor has access to certain properties and most importantly,
- // this.$props.
- // we are storing the vnodes in variables here so that there's no need to
- // always pass args in super()
- currentVNode = vnode
- currentContextVNode = vnode.contextVNode
- const Component = vnode.tag as ComponentClass
- const instance = (vnode.children = new Component() as ComponentInstance)
-
- // then we finish the initialization by collecting properties set on the
- // instance
- const {
- $proxy,
- $options: { created, computed, watch }
- } = instance
- initializeState(instance, !Component.fromOptions)
- initializeComputed(instance, computed)
- initializeWatch(instance, watch)
- instance.$slots = currentVNode.slots || EMPTY_OBJ
-
- if (created) {
- callLifecycleHookWithHandler(created, $proxy, ErrorTypes.CREATED)
- }
-
- currentVNode = currentContextVNode = null
- return instance
-}
-
-// this is called inside the base component's constructor
-// it initializes all the way up to props so that they are available
-// inside the extended component's constructor
-export function initializeComponentInstance(instance: ComponentInstance) {
- if (__DEV__ && currentVNode === null) {
- throw new Error(
- `Component classes are not meant to be manually instantiated.`
- )
- }
-
- instance.$options = resolveComponentOptionsFromClass(instance.constructor)
- instance.$parentVNode = currentVNode as MountedVNode
-
- // renderProxy
- const proxy = (instance.$proxy = createRenderProxy(instance))
-
- // parent chain management
- if (currentContextVNode !== null) {
- // locate first non-functional parent
- while (currentContextVNode !== null) {
- if ((currentContextVNode.flags & VNodeFlags.COMPONENT_STATEFUL) > 0) {
- const parentComponent = (currentContextVNode as VNode)
- .children as ComponentInstance
- instance.$parent = parentComponent.$proxy
- instance.$root = parentComponent.$root
- parentComponent.$children.push(proxy)
- break
- }
- currentContextVNode = currentContextVNode.contextVNode
- }
- } else {
- instance.$root = proxy
- }
-
- // beforeCreate hook is called right in the constructor
- const { beforeCreate, props } = instance.$options
- if (beforeCreate) {
- callLifecycleHookWithHandler(beforeCreate, proxy, ErrorTypes.BEFORE_CREATE)
- }
- initializeProps(instance, props, (currentVNode as VNode).data)
-}
-
-export let isRendering = false
-
-export function renderInstanceRoot(instance: ComponentInstance): VNode {
- let vnode
- const {
- $options: { hooks },
- render,
- $proxy,
- $props,
- $slots,
- $attrs,
- $parentVNode
- } = instance
- try {
- setCurrentInstance(instance)
- if (hooks) {
- instance._hookProps = hooks.call($proxy, $props) || null
- }
- if (__DEV__) {
- isRendering = true
- }
- vnode = render.call($proxy, $props, $slots, $attrs, $parentVNode)
- if (__DEV__) {
- isRendering = false
- }
- unsetCurrentInstance()
- } catch (err) {
- handleError(err, instance, ErrorTypes.RENDER)
- }
- return normalizeComponentRoot(vnode, $parentVNode)
-}
-
-export function renderFunctionalRoot(vnode: VNode): VNode {
- const render = vnode.tag as FunctionalComponent
- const { 0: props, 1: attrs } = resolveProps(vnode.data, render.props)
- let subTree
- try {
- subTree = render(props, vnode.slots || EMPTY_OBJ, attrs, vnode)
- } catch (err) {
- handleError(err, vnode, ErrorTypes.RENDER)
- }
- return normalizeComponentRoot(subTree, vnode)
-}
-
-export function teardownComponentInstance(instance: ComponentInstance) {
- const parentComponent = instance.$parent && instance.$parent._self
- if (parentComponent && !parentComponent._unmounted) {
- parentComponent.$children.splice(
- parentComponent.$children.indexOf(instance.$proxy),
- 1
- )
- }
- stop(instance._update)
- teardownComputed(instance)
- teardownWatch(instance)
-}
-
-function normalizeComponentRoot(
- vnode: any,
- componentVNode: VNode | null
-): VNode {
- if (vnode == null) {
- vnode = createTextVNode('')
- } else if (!isObject(vnode)) {
- vnode = createTextVNode(vnode + '')
- } else if (isArray(vnode)) {
- if (vnode.length === 1) {
- vnode = normalizeComponentRoot(vnode[0], componentVNode)
- } else {
- vnode = createFragment(vnode)
- }
- } else {
- const { el, flags } = vnode
- if (
- componentVNode &&
- (flags & VNodeFlags.COMPONENT || flags & VNodeFlags.ELEMENT)
- ) {
- if (el) {
- vnode = cloneVNode(vnode as VNode)
- }
- if (flags & VNodeFlags.COMPONENT) {
- vnode.parentVNode = componentVNode
- }
- } else if (el) {
- vnode = cloneVNode(vnode as VNode)
- }
- }
- return vnode
-}
-
-export function shouldUpdateComponent(
- prevVNode: VNode,
- nextVNode: VNode
-): boolean {
- const { data: prevProps, childFlags: prevChildFlags } = prevVNode
- const { data: nextProps, childFlags: nextChildFlags } = nextVNode
- // If has different slots content, or has non-compiled slots,
- // the child needs to be force updated.
- if (
- prevChildFlags !== nextChildFlags ||
- (nextChildFlags & ChildrenFlags.DYNAMIC_SLOTS) > 0
- ) {
- return true
- }
- if (prevProps === nextProps) {
- return false
- }
- if (prevProps === null) {
- return nextProps !== null
- }
- if (nextProps === null) {
- return prevProps !== null
- }
- const nextKeys = Object.keys(nextProps)
- if (nextKeys.length !== Object.keys(prevProps).length) {
- return true
- }
- for (let i = 0; i < nextKeys.length; i++) {
- const key = nextKeys[i]
- if (nextProps[key] !== prevProps[key]) {
- return true
- }
- }
- return false
-}
-
-// DEV only
-export function getReasonForComponentUpdate(
- prevVNode: VNode,
- nextVNode: VNode
-): any {
- const reasons = []
- const { childFlags: prevChildFlags } = prevVNode
- const { childFlags: nextChildFlags } = nextVNode
- if (
- prevChildFlags !== nextChildFlags ||
- (nextChildFlags & ChildrenFlags.DYNAMIC_SLOTS) > 0
- ) {
- reasons.push({
- type: `slots may have changed`,
- tip: `use function slots + $stable: true to avoid slot-triggered child updates.`
- })
- }
- const prevProps = prevVNode.data || EMPTY_OBJ
- const nextProps = nextVNode.data || EMPTY_OBJ
- for (const key in nextProps) {
- if (nextProps[key] !== prevProps[key]) {
- reasons.push({
- type: 'prop changed',
- key,
- value: nextProps[key],
- oldValue: prevProps[key]
- })
- }
- }
- for (const key in prevProps) {
- if (!(key in nextProps)) {
- reasons.push({
- type: 'prop changed',
- key,
- value: undefined,
- oldValue: prevProps[key]
- })
- }
- }
- return {
- type: 'triggered by parent',
- reasons
- }
-}
-
-export function createComponentClassFromOptions(
- options: ComponentOptions
-): ComponentClass {
- class AnonymousComponent extends Component {
- static options = options
- // indicate this component was created from options
- static fromOptions = true
- }
- const proto = AnonymousComponent.prototype as any
- for (const key in options) {
- const value = options[key]
- if (key === 'render') {
- if (__COMPAT__) {
- options.render = function() {
- return value.call(this, h)
- }
- }
- // so that we can call instance.render directly
- proto.render = options.render
- } else if (key === 'computed') {
- // create computed setters on prototype
- // (getters are handled by the render proxy)
- for (const computedKey in value) {
- const computed = value[computedKey]
- const set = isObject(computed) && computed.set
- if (set) {
- Object.defineProperty(proto, computedKey, {
- configurable: true,
- set
- })
- }
- }
- } else if (key === 'methods') {
- for (const method in value) {
- if (__DEV__ && proto.hasOwnProperty(method)) {
- warn(
- `Object syntax contains method name that conflicts with ` +
- `lifecycle hook: "${method}"`
- )
- }
- proto[method] = value[method]
- }
- } else if (__COMPAT__) {
- if (key === 'name') {
- options.displayName = value
- } else if (key === 'render') {
- options.render = function() {
- return value.call(this, h)
- }
- } else if (key === 'beforeDestroy') {
- options.beforeUnmount = value
- } else if (key === 'destroyed') {
- options.unmounted = value
- }
- }
- }
- return AnonymousComponent as ComponentClass
-}
VNodeChildren
} from './vdom'
import { ComponentInstance } from './component'
+import {
+ createComponentInstance,
+ teardownComponentInstance
+} from './componentInstance'
import {
renderInstanceRoot,
renderFunctionalRoot,
- createComponentInstance,
- teardownComponentInstance,
shouldUpdateComponent,
getReasonForComponentUpdate
-} from './componentUtils'
+} from './componentRenderUtils'
import { KeepAliveSymbol } from './optional/keepAlive'
import { pushWarningContext, popWarningContext, warn } from './warning'
import { resolveProps } from './componentProps'
// Internal API, for libraries or renderers that need to perform low level work
export * from './componentOptions'
-export {
- createComponentInstance,
- createComponentClassFromOptions
-} from './componentUtils'
+export { createComponentInstance } from './componentInstance'
import { Component } from '../component'
-import { createComponentClassFromOptions } from '../componentUtils'
+import { createComponentClassFromOptions } from '../componentOptions'
import {
ComponentOptions,
resolveComponentOptionsFromClass,
FunctionalComponent
} from './component'
import { VNodeFlags, ChildrenFlags } from './flags'
-import { createComponentClassFromOptions } from './componentUtils'
+import { createComponentClassFromOptions } from './componentOptions'
import { EMPTY_OBJ, isObject, isArray, isFunction, isString } from '@vue/shared'
import { RawChildrenType, RawSlots } from './h'
import { FunctionalHandle } from './createRenderer'