refactor: remove unnecessary functional wrapper + delay for async components

This commit is contained in:
Evan You 2018-09-25 22:25:18 -04:00
parent 2c753388c3
commit 9e62c3bd9a
4 changed files with 97 additions and 93 deletions

View File

@ -1,10 +1,10 @@
import { Slots, VNodeData } from './vdom' import { Slots } from './vdom'
import { MountedComponent } from './component' import { MountedComponent } from './component'
export type Data = Record<string, any> export type Data = Record<string, any>
export interface RenderFunction<P = Data> { export interface RenderFunction<P = Data> {
(props: P, slots: Slots, attrs: Data, rawData: VNodeData | null): any (props: P, slots: Slots, attrs: Data): any
} }
export interface ComponentOptions<D = Data, P = Data> { export interface ComponentOptions<D = Data, P = Data> {

View File

@ -284,7 +284,7 @@ export function createRenderer(options: RendererOptions) {
const render = tag as FunctionalComponent const render = tag as FunctionalComponent
const { props, attrs } = resolveProps(data, render.props, render) const { props, attrs } = resolveProps(data, render.props, render)
const subTree = (vnode.children = normalizeComponentRoot( const subTree = (vnode.children = normalizeComponentRoot(
render(props, slots || EMPTY_OBJ, attrs || EMPTY_OBJ, data), render(props, slots || EMPTY_OBJ, attrs || EMPTY_OBJ),
vnode, vnode,
attrs, attrs,
render.inheritAttrs render.inheritAttrs
@ -581,7 +581,7 @@ export function createRenderer(options: RendererOptions) {
if (shouldUpdate) { if (shouldUpdate) {
const { props, attrs } = resolveProps(nextData, render.props, render) const { props, attrs } = resolveProps(nextData, render.props, render)
const nextTree = (nextVNode.children = normalizeComponentRoot( const nextTree = (nextVNode.children = normalizeComponentRoot(
render(props, nextSlots || EMPTY_OBJ, attrs || EMPTY_OBJ, nextData), render(props, nextSlots || EMPTY_OBJ, attrs || EMPTY_OBJ),
nextVNode, nextVNode,
attrs, attrs,
render.inheritAttrs render.inheritAttrs
@ -1167,6 +1167,10 @@ export function createRenderer(options: RendererOptions) {
(__COMPAT__ && (parentVNode.children as MountedComponent)) || (__COMPAT__ && (parentVNode.children as MountedComponent)) ||
createComponentInstance(parentVNode, Component, parentComponent) createComponentInstance(parentVNode, Component, parentComponent)
if (instance.beforeMount) {
instance.beforeMount.call(instance.$proxy)
}
const queueUpdate = (instance.$forceUpdate = () => { const queueUpdate = (instance.$forceUpdate = () => {
queueJob(instance._updateHandle, flushHooks) queueJob(instance._updateHandle, flushHooks)
}) })
@ -1206,9 +1210,6 @@ export function createRenderer(options: RendererOptions) {
instance: MountedComponent, instance: MountedComponent,
ref: Ref | null ref: Ref | null
) { ) {
if (instance.beforeMount) {
instance.beforeMount.call(instance.$proxy)
}
if (ref) { if (ref) {
mountRef(ref, instance) mountRef(ref, instance)
} }

View File

@ -1,6 +1,6 @@
import { ChildrenFlags } from '../flags' import { ChildrenFlags } from '../flags'
import { createComponentVNode, VNodeData } from '../vdom' import { createComponentVNode, Slots } from '../vdom'
import { Component, ComponentType, FunctionalComponent } from '../component' import { Component, ComponentType, ComponentClass } from '../component'
export interface AsyncComponentFactory { export interface AsyncComponentFactory {
(): Promise<ComponentType> (): Promise<ComponentType>
@ -22,92 +22,90 @@ export type AsyncComponentOptions =
interface AsyncContainerData { interface AsyncContainerData {
comp: ComponentType | null comp: ComponentType | null
err: Error | null err: Error | null
delayed: boolean
timedOut: 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
}
}
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)
}
}
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
}
}
}
export function createAsyncComponent( export function createAsyncComponent(
options: AsyncComponentOptions options: AsyncComponentOptions
): FunctionalComponent { ): ComponentClass {
if (typeof options === 'function') { if (typeof options === 'function') {
options = { factory: options } options = { factory: options }
} }
return (_, __, ___, rawData) =>
createComponentVNode( const {
AsyncContainer, factory,
{ options, rawData }, timeout,
null, delay = 200,
ChildrenFlags.NO_CHILDREN loading: loadingComp,
) error: errorComp
} = options
return class AsyncContainer extends Component<AsyncContainerData> {
data() {
return {
comp: null,
err: null,
delayed: false,
timedOut: false
}
}
// 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)
}
}
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
} }

View File

@ -123,7 +123,7 @@ export function createElementVNode(
export function createComponentVNode( export function createComponentVNode(
comp: any, comp: any,
data: VNodeData | null, data: VNodeData | null,
children: VNodeChildren, children: VNodeChildren | Slots,
childFlags: ChildrenFlags, childFlags: ChildrenFlags,
key?: Key | null, key?: Key | null,
ref?: Ref | null ref?: Ref | null
@ -169,7 +169,9 @@ export function createComponentVNode(
// slots // slots
let slots: any let slots: any
if (childFlags === ChildrenFlags.UNKNOWN_CHILDREN) { if (childFlags === ChildrenFlags.STABLE_SLOTS) {
slots = children
} else if (childFlags === ChildrenFlags.UNKNOWN_CHILDREN) {
childFlags = children childFlags = children
? ChildrenFlags.DYNAMIC_SLOTS ? ChildrenFlags.DYNAMIC_SLOTS
: ChildrenFlags.NO_CHILDREN : ChildrenFlags.NO_CHILDREN
@ -361,9 +363,12 @@ export function normalizeVNodes(
// ensure all slot functions return Arrays // ensure all slot functions return Arrays
function normalizeSlots(slots: { [name: string]: any }): Slots { 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) { for (const name in slots) {
normalized[name] = (...args) => normalizeSlot(slots[name](...args)) normalized[name] = (...args: any[]) => normalizeSlot(slots[name](...args))
} }
return normalized return normalized
} }