const render = tag as FunctionalComponent
const { props, attrs } = resolveProps(data, render.props, render)
const subTree = (vnode.children = normalizeComponentRoot(
- render(props, slots || EMPTY_OBJ, attrs || EMPTY_OBJ, data),
+ render(props, slots || EMPTY_OBJ, attrs || EMPTY_OBJ),
vnode,
attrs,
render.inheritAttrs
if (shouldUpdate) {
const { props, attrs } = resolveProps(nextData, render.props, render)
const nextTree = (nextVNode.children = normalizeComponentRoot(
- render(props, nextSlots || EMPTY_OBJ, attrs || EMPTY_OBJ, nextData),
+ render(props, nextSlots || EMPTY_OBJ, attrs || EMPTY_OBJ),
nextVNode,
attrs,
render.inheritAttrs
(__COMPAT__ && (parentVNode.children as MountedComponent)) ||
createComponentInstance(parentVNode, Component, parentComponent)
+ if (instance.beforeMount) {
+ instance.beforeMount.call(instance.$proxy)
+ }
+
const queueUpdate = (instance.$forceUpdate = () => {
queueJob(instance._updateHandle, flushHooks)
})
instance: MountedComponent,
ref: Ref | null
) {
- if (instance.beforeMount) {
- instance.beforeMount.call(instance.$proxy)
- }
if (ref) {
mountRef(ref, instance)
}
import { ChildrenFlags } from '../flags'
-import { createComponentVNode, VNodeData } from '../vdom'
-import { Component, ComponentType, FunctionalComponent } from '../component'
+import { createComponentVNode, Slots } from '../vdom'
+import { Component, ComponentType, ComponentClass } from '../component'
export interface AsyncComponentFactory {
(): Promise<ComponentType>
interface AsyncContainerData {
comp: ComponentType | null
err: Error | null
+ delayed: boolean
timedOut: boolean
}
-interface AsyncContainerProps {
- options: AsyncComponentFullOptions
- rawData: VNodeData | null
-}
-
-export class AsyncContainer extends Component<
- AsyncContainerData,
- AsyncContainerProps
-> {
- data() {
- return {
- comp: null,
- err: null,
- timedOut: false
- }
+export function createAsyncComponent(
+ options: AsyncComponentOptions
+): ComponentClass {
+ if (typeof options === 'function') {
+ options = { factory: options }
}
- created() {
- const { factory, timeout } = this.$props.options
- if (factory.resolved) {
- this.comp = factory.resolved
- } else {
- factory()
- .then(resolved => {
- this.comp = factory.resolved = resolved
- })
- .catch(err => {
- this.err = err
- })
- }
- if (timeout != null) {
- setTimeout(() => {
- this.timedOut = true
- }, timeout)
+ const {
+ factory,
+ timeout,
+ delay = 200,
+ loading: loadingComp,
+ error: errorComp
+ } = options
+
+ return class AsyncContainer extends Component<AsyncContainerData> {
+ data() {
+ return {
+ comp: null,
+ err: null,
+ delayed: false,
+ timedOut: false
+ }
}
- }
- render(props: AsyncContainerProps) {
- if (this.err || (this.timedOut && !this.comp)) {
- const error =
- this.err ||
- new Error(`Async component timed out after ${props.options.timeout}ms.`)
- const errorComp = props.options.error
- return errorComp
- ? createComponentVNode(
- errorComp,
- { error },
- null,
- ChildrenFlags.NO_CHILDREN
- )
- : null
- } else if (this.comp) {
- return createComponentVNode(
- this.comp,
- props.rawData,
- null,
- ChildrenFlags.UNKNOWN_CHILDREN
- )
- } else {
- const loadingComp = props.options.loading
- return loadingComp
- ? createComponentVNode(
- loadingComp,
- null,
- null,
- ChildrenFlags.NO_CHILDREN
- )
- : null
+ // doing this in beforeMount so this is non-SSR only
+ beforeMount() {
+ if (factory.resolved) {
+ this.comp = factory.resolved
+ } else {
+ factory()
+ .then(resolved => {
+ this.comp = factory.resolved = resolved
+ })
+ .catch(err => {
+ this.err = err
+ })
+ }
+ if (timeout != null) {
+ setTimeout(() => {
+ this.timedOut = true
+ }, timeout)
+ }
+ if (delay != null) {
+ this.delayed = true
+ setTimeout(() => {
+ this.delayed = false
+ }, delay)
+ }
}
- }
-}
-export function createAsyncComponent(
- options: AsyncComponentOptions
-): FunctionalComponent {
- if (typeof options === 'function') {
- options = { factory: options }
- }
- return (_, __, ___, rawData) =>
- createComponentVNode(
- AsyncContainer,
- { options, rawData },
- null,
- ChildrenFlags.NO_CHILDREN
- )
+ render(props: any, slots: Slots) {
+ if (this.err || (this.timedOut && !this.comp)) {
+ const error =
+ this.err || new Error(`Async component timed out after ${timeout}ms.`)
+ return errorComp
+ ? createComponentVNode(
+ errorComp,
+ { error },
+ null,
+ ChildrenFlags.NO_CHILDREN
+ )
+ : null
+ } else if (this.comp) {
+ return createComponentVNode(
+ this.comp,
+ props,
+ slots,
+ ChildrenFlags.STABLE_SLOTS
+ )
+ } else {
+ return loadingComp && !this.delayed
+ ? createComponentVNode(
+ loadingComp,
+ null,
+ null,
+ ChildrenFlags.NO_CHILDREN
+ )
+ : null
+ }
+ }
+ } as ComponentClass
}
export function createComponentVNode(
comp: any,
data: VNodeData | null,
- children: VNodeChildren,
+ children: VNodeChildren | Slots,
childFlags: ChildrenFlags,
key?: Key | null,
ref?: Ref | null
// slots
let slots: any
- if (childFlags === ChildrenFlags.UNKNOWN_CHILDREN) {
+ if (childFlags === ChildrenFlags.STABLE_SLOTS) {
+ slots = children
+ } else if (childFlags === ChildrenFlags.UNKNOWN_CHILDREN) {
childFlags = children
? ChildrenFlags.DYNAMIC_SLOTS
: ChildrenFlags.NO_CHILDREN
// ensure all slot functions return Arrays
function normalizeSlots(slots: { [name: string]: any }): Slots {
- const normalized: Slots = {}
+ if (slots._normalized) {
+ return slots
+ }
+ const normalized = { _normalized: true } as any
for (const name in slots) {
- normalized[name] = (...args) => normalizeSlot(slots[name](...args))
+ normalized[name] = (...args: any[]) => normalizeSlot(slots[name](...args))
}
return normalized
}